package com.yourenbang.lingyun.transaction.web.service.impl.manage;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.model.OSSObject;
import com.google.common.collect.Maps;
import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.constants.DeleteFlag;
import com.yourenbang.lingyun.constants.enums.ChannelIdEnums;
import com.yourenbang.lingyun.constants.enums.PlatNoEnums;
import com.yourenbang.lingyun.constants.enums.UploadTypeEnum;
import com.yourenbang.lingyun.constants.enums.fund.AccountType;
import com.yourenbang.lingyun.constants.enums.mq.MqEnum;
import com.yourenbang.lingyun.constants.enums.mq.SendMqTypeEnums;
import com.yourenbang.lingyun.constants.enums.pay.PayRateEnum;
import com.yourenbang.lingyun.constants.enums.table.MerchantUpdateEnum;
import com.yourenbang.lingyun.constants.enums.table.PayTransactionEnum;
import com.yourenbang.lingyun.constants.enums.table.PayTransactionSupplementEnum;
import com.yourenbang.lingyun.core.annotations.apiRequestLimited.NoRepeatSubmit;
import com.yourenbang.lingyun.core.base.bean.OSSClientUtil;
import com.yourenbang.lingyun.core.config.datasource.DataSources;
import com.yourenbang.lingyun.core.config.datasource.RoutingDataSource;
import com.yourenbang.lingyun.core.message.JLResult;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.support.PageFrom;
import com.yourenbang.lingyun.core.util.*;
import com.yourenbang.lingyun.core.util.date.DateFormat;
import com.yourenbang.lingyun.core.util.date.LocalDateUtils;
import com.yourenbang.lingyun.core.util.format.CharsetKit;
import com.yourenbang.lingyun.core.util.http.HttpClientUtil;
import com.yourenbang.lingyun.domain.*;
import com.yourenbang.lingyun.mapper.*;
import com.yourenbang.lingyun.openapi.api.model.dto.client.ClientInfo;
import com.yourenbang.lingyun.openapi.api.service.client.ClientService;
import com.yourenbang.lingyun.transaction.config.AcmGlobalParams;
import com.yourenbang.lingyun.transaction.config.AcmTransactionParams;
import com.yourenbang.lingyun.transaction.constant.LklTransTypeEnum;
import com.yourenbang.lingyun.transaction.constant.TransactionConstant;
import com.yourenbang.lingyun.transaction.mapper.MePayTransactionSupplementMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionMerchantMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionPayTransactionIndexMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionPayTransactionMapper;
import com.yourenbang.lingyun.transaction.util.DateUtil;
import com.yourenbang.lingyun.transaction.util.HttpHolderUtil;
import com.yourenbang.lingyun.transaction.web.domain.dto.PayTransactionPO;
import com.yourenbang.lingyun.transaction.web.domain.dto.UserAllResponse;
import com.yourenbang.lingyun.transaction.web.domain.dto.fund.AccountQueryDTO;
import com.yourenbang.lingyun.transaction.web.domain.dto.fund.FundQueryGroupVO;
import com.yourenbang.lingyun.transaction.web.domain.dto.manage.*;
import com.yourenbang.lingyun.transaction.web.domain.dto.pay.jlpay.TradePushDTO;
import com.yourenbang.lingyun.transaction.web.domain.dto.pay.lakala.LakalaEposPayDTO;
import com.yourenbang.lingyun.transaction.web.domain.dto.yxy.LdysTradeDTO;
import com.yourenbang.lingyun.transaction.web.domain.dto.yxy.M40ExcelTradeInfoDTO;
import com.yourenbang.lingyun.transaction.web.domain.dto.yxy.TransactionCountYxyDTO;
import com.yourenbang.lingyun.transaction.web.domain.vo.PayTransactionVO;
import com.yourenbang.lingyun.transaction.web.domain.vo.fund.AccountQueryGroupVO;
import com.yourenbang.lingyun.transaction.web.domain.vo.manage.*;
import com.yourenbang.lingyun.transaction.web.domain.vo.pay.dy.DyTransactionVO;
import com.yourenbang.lingyun.transaction.web.service.fund.AccountService;
import com.yourenbang.lingyun.transaction.web.service.impl.pay.klks.KlksPayConfig;
import com.yourenbang.lingyun.transaction.web.service.impl.pay.transaction.PayTransactionConfig;
import com.yourenbang.lingyun.transaction.web.service.manage.ManageTradeService;
import com.yourenbang.lingyun.transaction.web.service.yxy.YxyTradeService;
import com.yourenbang.mcenter.api.model.dto.MqSendMessage;
import com.yourenbang.mcenter.api.service.McenterMqService;
import com.yourenbang.transaction.api.model.dto.manage.QueryClientFirstPageStatisticsDTO;
import com.yourenbang.transaction.api.model.dto.manage.QueryClientPledgeListDTO;
import com.yourenbang.transaction.api.model.vo.manage.QueryClientHomePageStatisticsVO;
import com.yourenbang.transaction.api.model.vo.manage.QueryClientPledgeListVO;
import com.yourenbang.transaction.api.model.vo.manage.QueryClientPledgeStatisticsChannelVO;
import com.yourenbang.transaction.api.model.vo.manage.QueryClientPledgeStatisticsVO;
import com.yourenbang.transaction.api.model.vo.pay.PayTransaction;
import com.yourenbang.transaction.api.service.pay.PayTypeApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.zip.ZipInputStream;

/**
 * @Description
 * @Date 2021/1/4 13:42
 * @Created by Jason
 */
@Service
@Slf4j
public class ManageTradeServiceImpl implements ManageTradeService {

    private static final String DEAL_TRADE_LAKALA_P42_URL = "/pay/epos/saveEpos";
    private static final String DEAL_TRADE_LAKALA_URL = "/pay/p35/saveP35";
    private static final String DEAL_TRADE_SFT_URL = "/transaction/yxy/callback/notice/sft";
    private static final String DEAL_TRADE_JL_URL = "/transaction/yxy/callback/trade/jl";
    private static final String DEAL_TRADE_DY_URL = "/transaction/yxy/callback/trade/dy";
    private static final String DEAL_TRADE_Y40_URL = "/transaction/yxy/callback/trade/ld";
    private static final String M40_TRADE_FILE_LIST="m40_trade_file_list";
    private static final String REMARKS = "记录卡类型与实际卡类型不一致，手动修复";
    private static final String DY_PLEDGE_CASH = "VIPPAY";

    /**
     * m40 特殊卡类型
     */
    private static final String M40_NEI_KA_CREDIT_NAME= "内卡贷记卡" ;
    private static final String M40_WEIXIN_DEBIT_NAME= "微信借记卡" ;

    @Autowired
    private TransactionPayTransactionMapper transactionPayTransactionMapper;
    @Autowired
    private PayTransactionIndexMapper payTransactionIndexMapper;
    @Autowired
    private TransactionMerchantMapper transactionMerchantMapper;
    @Autowired
    private UserBaseMapper userBaseMapper;
    @Resource
    private TransactionPayTransactionIndexMapper transactionPayTransactionIndexMapper;
    @Resource
    private AcmTransactionParams acmTransactionParams;
    @Resource
    private AcmGlobalParams acmGlobalParams;
    @Resource
    private PayTransactionSupplementMapper payTransactionSupplementMapper;
    @Autowired
    private AccountService accountService;
    @Autowired
    private ClientService clientService;
    @Autowired
    private MePayTransactionSupplementMapper mePayTransactionSupplementMapper;
    @Autowired
    private OSSClientUtil ossClientUtil;
    @Autowired
    private YxyTradeService yxyTradeService;
    @Autowired
    Merchant21Mapper merchant21Mapper;
    @Autowired
    private UserBaseExtMapper userBaseExtMapper;
    @Autowired
    private MerchantUpdate20Mapper merchantUpdate20Mapper;
    @Autowired
    private MerchantUpdate21Mapper merchantUpdate21Mapper;
    @Autowired
    private McenterMqService mcenterMqService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Value("${m40.import.error.notify}")
    private String m40ImportErrorNotify;
    @Autowired
    private M40OrderNoRelationMapper m40OrderNoRelationMapper;
    @Autowired
    private PayTypeApiService payTypeApiService;
    /**
     * 创建可缓存的线程池
     */
    @Resource(name = "taskExecutor")
    ThreadPoolTaskExecutor taskExecutor;

    @Override
    public Result tradeList(TradeManageDTO dto) {
        KLog.info("[交易管理列表]入参:{}", dto.toString());
        if (StringUtils.isAllBlank(dto.getOrderNo(), dto.getMerchantNo())) {
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR);
        }
        if (StringUtils.isNotBlank(dto.getMachineType())) {
            dto.setPlatNo(ChannelIdEnums.getChannelIdEnums(Short.valueOf(dto.getMachineType())).getPlatNo());
            dto.setChannelId(dto.getMachineType());
        }

        String userBaseId = "";
        if (StringUtils.isNotBlank(dto.getOrderNo())) {
            PayTransactionIndexExample example = new PayTransactionIndexExample();
            example.createCriteria().andOrderNoEqualTo(dto.getOrderNo()).andPlatNoEqualTo(dto.getPlatNo())
                    .andIsDelEqualTo(false);
            List<PayTransactionIndex> payTransactionIndices = payTransactionIndexMapper.selectByExample(example);
            if (payTransactionIndices.isEmpty()) {
                return Result.builder().successPage(new ArrayList());
            } else {
                userBaseId = payTransactionIndices.get(0).getUserBaseId();
            }
        } else if (StringUtils.isNotBlank(dto.getMerchantNo())) {
            userBaseId = transactionMerchantMapper.queryUserBaseIdByMerchantNo(dto.getPlatNo(), dto.getMerchantNo());

            if (StringUtils.isBlank(userBaseId)) {
                return Result.builder().successPage(new ArrayList());
            }
        }
        PageFrom.startPage(dto.getPageNum(),dto.getPageSize());
        List<ManageTradeVO> manageTradeVOS = transactionPayTransactionMapper
                .queryMangeTradeList(PayTransactionConfig.getTransTableIndex(userBaseId), dto);
        manageTradeVOS.forEach(manageTradeVO -> {
            manageTradeVO.setPhone(userBaseMapper.selectByPrimaryKey(manageTradeVO.getUserBaseId()).getPhone());
            manageTradeVO.setCardFlagName(PayTransactionEnum.CardFlagEnum.getMsgByCode(manageTradeVO.getCardFlag()));
            manageTradeVO.setChannelIdName(ChannelIdEnums.getChannelNameByCode(new Short(manageTradeVO.getChannelId())));
            manageTradeVO.setStatusName(PayTransactionEnum.TransactionStatus.getMsgByCode(manageTradeVO.getStatus()));
        });

        return Result.builder().successPage(manageTradeVOS);
    }

    @Override
    public Result clientTradeList(TradeManageDTO dto) {
        KLog.info("[下游机构后台交易列表]入参:{}", dto.toString());
        String checkConditionResult = checkTradeCondition(dto);
        if (StringUtil.isNotBlank(checkConditionResult)) {
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR, checkConditionResult);
        }
        if(StringUtils.isNotEmpty(dto.getTerminalSn())) {
            String merchantNo = transactionMerchantMapper.getMerchantNoByTerminalSn(PlatNoEnums.YXY.getCode(), dto.getTerminalSn().trim());
            if (StringUtils.isEmpty(merchantNo) || (StringUtils.isNotEmpty(dto.getMerchantNo()) && !merchantNo.equals(dto.getMerchantNo()))) {
                return Result.builder().successPage(Collections.emptyList());
            } else {
                dto.setMerchantNo(merchantNo);
            }
        }
        if (dto.getPageSize() > 0) {
            PageFrom.startPage(dto.getPageNum(), dto.getPageSize());
        }
        long currentTime=System.currentTimeMillis();
        List<ManageTradeVO> manageTradeVOList = transactionPayTransactionMapper.queryMangeClientTradeList(dto);
        KLog.info("[下游机构后台交易列表]主表查询时间{}", System.currentTimeMillis()-currentTime);
        currentTime=System.currentTimeMillis();
        if (manageTradeVOList.size() > 0) {
            Map<Integer, List<String>> tableIndexOrderNos = Maps.newHashMapWithExpectedSize(dto.getPageSize() > 0 ? dto.getPageSize() : 100);
            for (ManageTradeVO manageTradeVO : manageTradeVOList) {
                Integer tableIndex = manageTradeVO.getTableIndex();
                if (tableIndexOrderNos.containsKey(tableIndex)) {
                    tableIndexOrderNos.get(tableIndex).add(manageTradeVO.getOrderNo());
                } else {
                    List<String> orderNoList = new LinkedList<>();
                    orderNoList.add(manageTradeVO.getOrderNo());
                    tableIndexOrderNos.put(tableIndex, orderNoList);
                }
            }
            //循环添加任务
            List<Future<List<PayTransaction>>> taskList = new ArrayList<>();
            for (Map.Entry<Integer, List<String>> integerListEntry : tableIndexOrderNos.entrySet()) {
                TradeManageDTO queryDto = new TradeManageDTO();
                queryDto.setOrderNos(integerListEntry.getValue());
                taskList.add(taskExecutor.submit(new DoQueryPayTransaction(integerListEntry.getKey(), queryDto)));
            }
            //循环获取执行结果
            for (Future<List<PayTransaction>> listListenableFuture : taskList) {
                try {
                    List<PayTransaction> payTransactionList = listListenableFuture.get();
                    if (payTransactionList != null && payTransactionList.size() > 0) {
                        for (PayTransaction payTransaction : payTransactionList) {
                            for (ManageTradeVO manageTradeVO : manageTradeVOList) {
                                if (manageTradeVO.getOrderNo().equals(payTransaction.getOrderNo())) {
                                    manageTradeVO.setName(payTransaction.getRealName());
                                    manageTradeVO.setPhone(payTransaction.getPhone());
                                    manageTradeVO.setRate(new BigDecimal(payTransaction.getRate()));
                                    manageTradeVO.setTerminalSn(payTransaction.getSn());
                                    break;
                                }
                            }
                        }
                    }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
            KLog.info("[下游机构后台交易列表]子表查询时间{}", System.currentTimeMillis()-currentTime);
        }
        return Result.builder().successPage(manageTradeVOList);
    }

    private String checkTradeCondition(TradeManageDTO dto){
        if (isClientTradeConditionAllBlank(dto)
                && StringUtils.isAnyBlank(dto.getTradeBeginTime(), dto.getTradeEndTime())) {
            return "请选择时间范围";
        }
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DateUtil.parsePatterns[0]);
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DateUtil.parsePatterns[1]);
        if(StringUtils.isNotEmpty(dto.getTradeBeginTime())){
            dto.setTradeBeginTime(LocalDate.parse(dto.getTradeBeginTime(), dateFormatter).atStartOfDay().format(dateTimeFormatter));
        }
        if(StringUtils.isNotEmpty(dto.getTradeEndTime())){
            dto.setTradeEndTime(LocalDate.parse(dto.getTradeEndTime(), dateFormatter).plusDays(1).atStartOfDay().format(dateTimeFormatter));
        }
        if(isClientTradeConditionAllBlank(dto)
                && LocalDateUtils.betweenDays(dto.getTradeBeginTime(), dto.getTradeEndTime()) > 31){
            return "时间范围不能大于31天";
        }
        return null;
    }

    @Override
    public Result clientTradeStatistics(TradeManageDTO dto) {
        String checkConditionResult = checkTradeCondition(dto);
        if (StringUtil.isNotBlank(checkConditionResult)) {
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR, checkConditionResult);
        }
        if (StringUtils.isNotEmpty(dto.getTerminalSn())) {
            String merchantNo = transactionMerchantMapper.getMerchantNoByTerminalSn(PlatNoEnums.YXY.getCode(), dto.getTerminalSn().trim());
            //获取到的商户号为空或者与输入的商户号不一致，则直接返回空集合
            boolean merchantNoMatchFail= StringUtils.isEmpty(merchantNo) || (StringUtils.isNotEmpty(dto.getMerchantNo()) && !merchantNo.equals(dto.getMerchantNo()));
            if (merchantNoMatchFail) {
                return Result.builder().success(new ManageClientTransactionStatisticsVO());
            } else {
                dto.setMerchantNo(merchantNo);
            }
        }
        List<ManageTradeAmountDTO> tradeAmountDTOS = transactionPayTransactionMapper.queryMangeClientTradeStatistics(dto);
        if (CollectionUtil.isEmpty(tradeAmountDTOS)) {
            return Result.builder().success(new ManageClientTransactionStatisticsVO());
        }

        ManageClientTransactionStatisticsVO transactionStatisticsVO = new ManageClientTransactionStatisticsVO();
        BigDecimal creditTransactionAmount = BigDecimal.ZERO;
        int creditTransactionCount = 0;
        BigDecimal debitTransactionAmount = BigDecimal.ZERO;
        int debitTransactionCount = 0;
        BigDecimal scanTransactionAmount = BigDecimal.ZERO;
        int scanTransactionCount = 0;

        for (ManageTradeAmountDTO tradeAmountDTO : tradeAmountDTOS) {
            String cardFlag=tradeAmountDTO.getCardFlag();
            if (StringUtil.equalsAny(cardFlag, PayTransactionEnum.CardFlagEnum.CREDIT_CARD.getCode(), PayTransactionEnum.CardFlagEnum.UNIONCODE_CREDIT.getCode(), "03")) {
                creditTransactionAmount = creditTransactionAmount.add(tradeAmountDTO.getAmount());
                creditTransactionCount += tradeAmountDTO.getCount();
            } else if (StringUtil.equalsAny(cardFlag, PayTransactionEnum.CardFlagEnum.BORROW_CARD.getCode(), PayTransactionEnum.CardFlagEnum.UNIONCODE_DEBIT.getCode())) {
                debitTransactionAmount = debitTransactionAmount.add(tradeAmountDTO.getAmount());
                debitTransactionCount += tradeAmountDTO.getCount();
            } else if (StringUtil.equalsAny(cardFlag, PayTransactionEnum.CardFlagEnum.ALIPAY.getCode(), PayTransactionEnum.CardFlagEnum.WECHAT.getCode())) {
                scanTransactionAmount = scanTransactionAmount.add(tradeAmountDTO.getAmount());
                scanTransactionCount += tradeAmountDTO.getCount();
            }
        }

        transactionStatisticsVO.setCreditTransactionAmount(NumberUtil.doBigDecimalDownFormat(creditTransactionAmount));
        transactionStatisticsVO.setCreditTransactionCount(Integer.toString(creditTransactionCount));
        transactionStatisticsVO.setDebitTransactionAmount(NumberUtil.doBigDecimalDownFormat(debitTransactionAmount));
        transactionStatisticsVO.setDebitTransactionCount(Integer.toString(debitTransactionCount));
        transactionStatisticsVO.setScanTransactionAmount(NumberUtil.doBigDecimalDownFormat(scanTransactionAmount));
        transactionStatisticsVO.setScanTransactionCount(Integer.toString(scanTransactionCount));

        return Result.builder().success(transactionStatisticsVO);
    }

    /**
     * 是否搜索条件都为空（用来判断如果搜索都为空，必须选择时间）
     * @param dto 输入参数
     * @return TRUE和 false
     */
    private boolean isClientTradeConditionAllBlank(TradeManageDTO dto) {
        return StringUtils.isAllBlank(dto.getMerchantNo(), dto.getOrderNo(), dto.getTerminalSn(), dto.getMerchantName());
    }


    @Override
    public Result tradeDetail(TradeManageDetailDTO dto) {
        KLog.info("后台交易详情入参:{}", dto.toString());
        PayTransactionVO payTransaction =
                transactionPayTransactionMapper.queryMangeTradeDetail(PayTransactionConfig.getTransTableIndex(dto.getUserBaseId()), dto);
        if(StringUtils.isNotBlank(dto.getA9UserBaseId())){
            payTransaction.setUserBaseId(null);
            payTransaction.setMerchantId(null);
        }
        return Result.builder().success(payTransaction);
    }

    @NoRepeatSubmit(time = 3)
    @Override
    public Result tradeSupplement(TradeSupplementDTO tradeSupplementDTO) {

        KLog.info("补录交易入参:{}", tradeSupplementDTO.toJSON());

        String orderNo = tradeSupplementDTO.getOrderNo();
        if (ChannelIdEnums.isEqualsAny(tradeSupplementDTO.getChannelId(),
                ChannelIdEnums.yxy_lkl_p40,
                ChannelIdEnums.yxy_lkl_p42,
                ChannelIdEnums.yxy_lkl_p45,
                ChannelIdEnums.k40,
                ChannelIdEnums.yxy_dy_d40,
                ChannelIdEnums.yxy_jl_m40,
                ChannelIdEnums.yxy_ldys_y40)){
            orderNo = tradeSupplementDTO.getTransactionNo();
        }
        PayTransactionSupplement payTransactionSupplement = new PayTransactionSupplement();
        payTransactionSupplement.setOrderNo(orderNo);
        String batchNo = tradeSupplementDTO.getBatchNo();
        if (StringUtils.isBlank(batchNo)
                || TradeSupplementDTO.IS_RE_SUPPLEMENT.equals(tradeSupplementDTO.getReSupplementFlag())){
            batchNo = getBatchNo();
        }
        payTransactionSupplement.setBatchNo(batchNo);
        payTransactionSupplement.setRequestParamDesc(tradeSupplementDTO.toJSON());
        payTransactionSupplement.setOperator(tradeSupplementDTO.getOperator());
        payTransactionSupplement.setMerchantNo(tradeSupplementDTO.getMerchantNo());
        payTransactionSupplement.setChannelId(tradeSupplementDTO.getChannelId());
        Short platNo = ChannelIdEnums.getChannelIdEnums(tradeSupplementDTO.getChannelId()).getPlatNo();
        payTransactionSupplement.setPlatNo(platNo);
        payTransactionSupplement.setBatchExcelPath(tradeSupplementDTO.getBatchExcelPath());

        if (ChannelIdEnums.no.isEquals(tradeSupplementDTO.getChannelId())
                || StringUtils.isBlank(tradeSupplementDTO.getCardFlag())
                || StringUtils.isBlank(tradeSupplementDTO.getTerminalSn())
                || tradeSupplementDTO.getTransactionAmount().compareTo(BigDecimal.ZERO) <= 0
                || Objects.isNull(tradeSupplementDTO.getTransactionTime())
                || tradeSupplementDTO.getTransactionTime().compareTo(LocalDateTime.now()) > 0) {
            payTransactionSupplement.setSupplementStatus(PayTransactionSupplementEnum.SupplementEnum.SUPPLEMENT_FAIL.getCode());
            int ins = payTransactionSupplementMapper.insertSelective(payTransactionSupplement);
            BizAssertUtils.assertOne(ins, "插入补录结果失败");
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR);
        }

        Result result = toSupplement(tradeSupplementDTO);

        if (result.isSuccess()) {
            payTransactionSupplement.setSupplementStatus(PayTransactionSupplementEnum.SupplementEnum.SUPPLEMENT_SUCCESS.getCode());
        } else {
            payTransactionSupplement.setSupplementStatus(PayTransactionSupplementEnum.SupplementEnum.SUPPLEMENT_FAIL.getCode());
        }
        int ins = payTransactionSupplementMapper.insertSelective(payTransactionSupplement);
        BizAssertUtils.assertOne(ins, "插入补录结果失败");
        return result;
    }

    @Override
    public void batchTradeSupplementTemplate() {
        List<Map<String, Object>> list = new ArrayList<>();
        HashMap<String, Object> exampleLakala = new HashMap<>();
        exampleLakala.put("channelName", "P40");
        exampleLakala.put("transactionTime", "2021-11-23 00:00:00");
        exampleLakala.put("orderNo", "无");
        exampleLakala.put("transactionNo", "21062610636019");
        exampleLakala.put("sysRef", "062610636019");
        exampleLakala.put("merchantNo", "8224520581202VX");
        exampleLakala.put("terminalSn", "00001102C8301183818");
        exampleLakala.put("terminalId", "C4442164");
        exampleLakala.put("transactionAmount", "150.00");
        exampleLakala.put("cardFlag", "微信");
        exampleLakala.put("transType", "无");
        exampleLakala.put("transactionFee", "0");
        exampleLakala.put("settlementAmount", "0");
        exampleLakala.put("merchantName", "无");
        exampleLakala.put("transactionCardNo", "无");
        list.add(exampleLakala);
        HashMap<String, Object> exampleSft = new HashMap<>();
        exampleSft.put("channelName", "S40");
        exampleSft.put("transactionTime", "2021-11-23 00:00:00");
        exampleSft.put("orderNo", "747752599");
        exampleSft.put("transactionNo", "无");
        exampleSft.put("sysRef", "无");
        exampleSft.put("merchantNo", "32026136");
        exampleSft.put("terminalSn", "000006026284401447");
        exampleSft.put("terminalId", "无");
        exampleSft.put("transactionAmount", "8200.0");
        exampleSft.put("cardFlag", "银联二维码");
        exampleSft.put("transType", "无");
        exampleSft.put("transactionFee", "49.2");
        exampleSft.put("settlementAmount", "8150.8");
        exampleSft.put("merchantName", "北京市悦宠杂货铺");
        exampleSft.put("transactionCardNo", "无");
        list.add(exampleSft);
        HashMap<String, Object> exampleJl = new HashMap<>();
        exampleJl.put("channelName", "L40");
        exampleJl.put("transactionTime", "2021-11-23 00:00:00");
        exampleJl.put("orderNo", "2101159020210515144647307023");
        exampleJl.put("transactionNo", "2101159020210515144647307023");
        exampleJl.put("sysRef", "151447901757");
        exampleJl.put("merchantNo", "N000000J1388X87");
        exampleJl.put("terminalSn", "C83EJ0046861");
        exampleJl.put("terminalId", "NX329OH7");
        exampleJl.put("transactionAmount", "149");
        exampleJl.put("cardFlag", "贷记卡");
        exampleJl.put("transType", "押金交易");
        exampleJl.put("transactionFee", "0055");
        exampleJl.put("settlementAmount", "0");
        exampleJl.put("merchantName", "无");
        exampleJl.put("transactionCardNo", "625969****1957");
        list.add(exampleJl);
        HashMap<String, Object> exampleDy = new HashMap<>();
        exampleDy.put("channelName", "D40");
        exampleDy.put("transactionTime", "2021-11-23 05:00:10");
        exampleDy.put("orderNo", "无");
        exampleDy.put("transactionNo", "DY404G15500001908");
        exampleDy.put("sysRef", "053779147455");
        exampleDy.put("merchantNo", "DY404G15500001908");
        exampleDy.put("terminalSn", "DY404G15500001908");
        exampleDy.put("terminalId", "57097846");
        exampleDy.put("transactionAmount", "149");
        exampleDy.put("cardFlag", "借记卡");
        exampleDy.put("transType", "无");
        exampleDy.put("transactionFee", "0");
        exampleDy.put("settlementAmount", "0");
        exampleDy.put("merchantName", "北京市悦宠杂货铺");
        exampleDy.put("transactionCardNo", "无");
        list.add(exampleDy);
        HashMap<String, Object> exampleJlM40 = new HashMap<>();
        exampleJlM40.put("channelName", "M40");
        exampleJlM40.put("transactionTime", "2021-11-22 00:00:00");
        exampleJlM40.put("orderNo", "2101159020210515144647307024");
        exampleJlM40.put("transactionNo", "2101159020210515144647307024");
        exampleJlM40.put("sysRef", "151447901758");
        exampleJlM40.put("merchantNo", "N000000J1388X88");
        exampleJlM40.put("terminalSn", "C83EJ0046862");
        exampleJlM40.put("terminalId", "NX329OH8");
        exampleJlM40.put("transactionAmount", "100");
        exampleJlM40.put("cardFlag", "贷记卡");
        exampleJlM40.put("transType", "押金交易");
        exampleJlM40.put("transactionFee", "0055");
        exampleJlM40.put("settlementAmount", "0");
        exampleJlM40.put("merchantName", "无");
        exampleJlM40.put("transactionCardNo", "625969****1957");
        list.add(exampleJlM40);
        HashMap<String, Object> exampleJlY40 = new HashMap<>();
        exampleJlY40.put("channelName", "Y40");
        exampleJlY40.put("transactionTime", "2021-11-22 00:00:00");
        exampleJlY40.put("orderNo", "");
        exampleJlY40.put("transactionNo", "2101159020210515144647307024");
        exampleJlY40.put("sysRef", "151447901758");
        exampleJlY40.put("merchantNo", "N000000J1388X88");
        exampleJlY40.put("terminalSn", "C83EJ0046862");
        exampleJlY40.put("terminalId", "NX329OH8");
        exampleJlY40.put("transactionAmount", "100");
        exampleJlY40.put("cardFlag", "贷记卡");
        exampleJlY40.put("transType", "押金交易");
        exampleJlY40.put("transactionFee", "0055");
        exampleJlY40.put("settlementAmount", "0");
        exampleJlY40.put("merchantName", "个体户****");
        exampleJlY40.put("transactionCardNo", "625969****1957");
        exampleJlY40.put("d0Fee", "300");
        list.add(exampleJlY40);
        shareBatchTradeSupplementTemplateExport(list);
    }

    @Override
    public Result batchTradeSupplement(MultipartFile file, String operator) {
        //页头检查
        Function<ArrayList<String>, String> headerCheck = (ArrayList<String> row) -> {
            List<String> headers = Arrays.asList("产品类型", "交易时间", "订单号", "流水号", "系统参考号", "商户号", "机具号", "终端号", "金额（元）", "卡类型", "交易类型", "手续费（元）", "结算金额", "商户名称", "交易卡号", "d0手续费");
            for (int i = 0; i < headers.size(); i++) {
                if (!row.get(i).equals(headers.get(i))) {
                    return "第" + (i + 1) + "列不是" + headers.get(i);
                }
            }
            return null;
        };

        Function<ExcelDataAndBatchDateDTO, TradeSupplementDTO> dealFunction=(excelDataAndBatchDateDTO)->{
            ArrayList<String> row=excelDataAndBatchDateDTO.getRow();
            TradeSupplementDTO tradeSupplementDTO=new TradeSupplementDTO();
            try {
                Short channelId = ChannelIdEnums.getChannelCodeByName(row.get(0));
                if (channelId == null) {
                    return null;
                }
                tradeSupplementDTO.setChannelId(channelId);
                LocalDateTime transactionTime = DateFormat.DATETIME_DEFAULT.parse(row.get(1));
                tradeSupplementDTO.setTransactionTime(transactionTime);
                tradeSupplementDTO.setOrderNo(row.get(2));
                tradeSupplementDTO.setTransactionNo(row.get(3));
                tradeSupplementDTO.setSysRef(row.get(4));
                tradeSupplementDTO.setMerchantNo(row.get(5));
                tradeSupplementDTO.setTerminalSn(row.get(6));
                tradeSupplementDTO.setTerminalId(row.get(7));
                if (StringUtils.isNotBlank(row.get(8))) {
                    tradeSupplementDTO.setTransactionAmount(new BigDecimal(row.get(8)));
                }
                String cardFlag = PayTransactionEnum.CardFlagEnum.getCodeByMsg(row.get(9));
                tradeSupplementDTO.setCardFlag(cardFlag);
                String transType = LklTransTypeEnum.getTransTypeByTypeName(row.get(10));
                tradeSupplementDTO.setTransType(transType);
                if (StringUtils.isNotBlank(row.get(11))) {
                    tradeSupplementDTO.setTransactionFee(new BigDecimal(row.get(11)));
                }
                if (StringUtils.isNotBlank(row.get(12))) {
                    tradeSupplementDTO.setSettlementAmount(new BigDecimal(row.get(12)));
                }
                tradeSupplementDTO.setMerchantName(row.get(13));
                tradeSupplementDTO.setTransactionCardNo(row.get(14));
                if (StringUtils.isNotBlank(row.get(15))) {
                    tradeSupplementDTO.setD0Fee(new BigDecimal(row.get(15)));
                }
                tradeSupplementDTO.setOperator(operator);
                tradeSupplementDTO.setBatchExcelPath(excelDataAndBatchDateDTO.getBatchExcelPath());
                tradeSupplementDTO.setBatchNo(excelDataAndBatchDateDTO.getBatchNo());
            } catch (Exception e){
                return null;
            }
            return tradeSupplementDTO;
        };

        return dealExcelTransactionDate(file, operator, dealFunction, headerCheck);
    }

    @Override
    public Result batchTradeSupplementM40(MultipartFile file, String operator, String mail) {
        Result<String> uploadResult = upload(file,UploadTypeEnum.BATCH_TRADE_SUPPLEMENT_EXCEL);
        if (!uploadResult.isSuccess()){
            return uploadResult;
        }
        KLog.info("批量补录M40任务执行:{}",uploadResult);

        if (StringUtil.isEmpty(file.getResource().getFilename()) || !file.getResource().getFilename().endsWith(".xlsx")) {
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR,"文件格式错误，请核对后再上传！");
        }

        if (StringUtil.isEmpty(mail)) {
            mail = m40ImportErrorNotify;
        }
        String finalMail = mail;

        taskExecutor.execute(() -> {
            try {
                importM40TradeFromInput(file.getInputStream(), uploadResult.getData(), operator, finalMail);
            } catch (IOException e) {
                KLog.error("批量补录M40任务执行, 读取文件失败,{}",e.getMessage());
            }
        });
        return Result.builder().desc("导入任务添加成功").success();
    }

    /**
     * 通过excelReader读取M40交易
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param operator 操作者
     * @param mail 发邮件的地址
     */
    private void importM40TradeFromInput(InputStream inputStream, String filePath, String operator, String mail) {
        List<M40ExcelTradeInfoDTO> sheet0 = new ArrayList<>();
        final String[] headerCheck = {""};
        EasyExcel.read(inputStream, M40ExcelTradeInfoDTO.class, new AnalysisEventListener<M40ExcelTradeInfoDTO>(){

            @Override
            public void invoke(M40ExcelTradeInfoDTO m40ExcelTradeInfoDTO, AnalysisContext analysisContext) {
                //如果页头有问题就不往下运行了
                if(StringUtil.isEmpty(headerCheck[0])){
                    sheet0.add(m40ExcelTradeInfoDTO);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
                List<String> headers = Arrays.asList("机身号", "商户号", "商户名", "交易时间", "统计日期", "刷卡金额", "手续费", "交易类型", "卡类型", "是否封顶", "合伙人账号", "合伙人姓名", "订单号");
                for (int i = 0; i < headers.size(); i++) {
                    if (!headMap.get(i).equals(headers.get(i))) {
                        headerCheck[0] ="校验失败, 第" + (i + 1) + "列不是" + headers.get(i);
                        break;
                    }
                }
            }
        }).sheet().doRead();
        if(StringUtil.isNotEmpty(headerCheck[0])){
            MailUtil.send(Collections.singletonList(mail),"M40交易Excel格式有误", headerCheck[0], "");
            return;
        }
        long startTime= System.currentTimeMillis();
        ArrayList<JSONObject> failList=new ArrayList<>();
        Result<JSONObject> result = doM40TransactionImport(sheet0, filePath, operator);
        String content;
        if (result.isSuccess()) {
            JSONObject jResult = result.getData();
            failList.addAll((ArrayList<JSONObject>) jResult.get("errorList"));
            long second = (System.currentTimeMillis() - startTime) / 1000;
            String secondStr = second < 60 ? second + "秒" : (second / 60) + "分" + (second % 60) + "秒";
            content = String.format("M40导入完成， 成功%d, 失败%d, 耗时%s", jResult.getInteger("successNum"), failList.size(), secondStr);
        } else {
            JSONObject jResult = new JSONObject();
            jResult.put("index", -1);
            jResult.put("msg", result.getDesc());
            jResult.put("batchNo", "");
            failList.add(jResult);
            content= "M40导入失败";
        }
        //错误大于0 邮件通知
        sendImportErrorMail(mail,content, failList);
    }

    /**
     * 发送导入异常邮件
     * @param mail 邮箱地址
     * @param failList 错误信息
     */
    private void sendImportErrorMail(String mail, String content, ArrayList<JSONObject> failList) {
        String excelName = "M40导入结果通知(" + DateUtil.getDate() + ").xlsx";
        String excelPath = System.getProperty("user.dir") + "/m40_import/";
        if(CollectionUtil.isNotEmpty(failList)){
            if (!createImportErrorExcel(excelPath, excelName, failList)) {
                KLog.error("生成M40导入结果通知Excel异常,入参{}\t{}\t{}", excelPath, excelName, failList);
            } else {
                MailUtil.send(Collections.singletonList(mail),
                        excelName,
                        content,
                        excelPath + excelName);
            }
        }else{
            MailUtil.send(Collections.singletonList(mail),
                    excelName,
                    content, null);
        }
    }

    @Override
    public boolean addM40TradeFileToRedis(String fileUrl) {
        try {
            Long result = redisTemplate.opsForList().leftPush(M40_TRADE_FILE_LIST, fileUrl);
            taskExecutor.execute(this::batchTradeSupplementM40FromRedis);
            return Objects.nonNull(result) && result > 0;
        } catch (Exception e) {
            KLog.warn("添加m40交易文件到redis失败，{}", e.getMessage());
            return false;
        }
    }

    @Override
    public void batchTradeSupplementM40FromRedis() {
        Long fileSize = redisTemplate.opsForList().size(M40_TRADE_FILE_LIST);
        if(Objects.isNull(fileSize)){
            fileSize=Long.valueOf("0");
        }
        KLog.info("redis中读取交易地址数量,{}", fileSize);
        if (fileSize == 0) {
            return;
        }
        while (Objects.nonNull(redisTemplate.opsForList().index(M40_TRADE_FILE_LIST, 0))) {
            String fileUrl = (String) redisTemplate.opsForList().index(M40_TRADE_FILE_LIST, 0);
            KLog.info("redis中读取交易地址,{}", fileUrl);
            if(StringUtil.isEmpty(fileUrl)){
                redisTemplate.opsForList().rightPop(M40_TRADE_FILE_LIST);
                continue;
            }
            InputStream input = null;
            try {
                URL url = new URL(fileUrl);
                input = url.openStream();
                ZipInputStream zipIn = new ZipInputStream(input, CharsetKit.CHARSET_GBK);
                if (Objects.nonNull(zipIn.getNextEntry())) {
                    importM40TradeFromInput(zipIn, fileUrl, "system", m40ImportErrorNotify);
                    KLog.info("redis中读取交易excel完成");
                }
            } catch (Exception e) {
                KLog.error("redis中读取交易excel失败,{}", e.getMessage());
            } finally {
                if (Objects.nonNull(input)) {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            redisTemplate.opsForList().rightPop(M40_TRADE_FILE_LIST);
        }
    }

    /**
     * 生成导入失败数据表
     * @author qinmingtao
     * @param excelPath Excel文件地址
     * @param excelName Excel文件名字
     * @param dealResults 数据集
     */
    private boolean createImportErrorExcel(String excelPath, String excelName, List<JSONObject> dealResults) {
        log.info("生成excel文件位置:{}/{}", excelPath, excelName);
        File file = new File(excelPath);
        if (!file.exists()) {
            if(!file.mkdir()) {
                return false;
            }
        }
        // 通过工具类创建writer
        ExcelWriter writer = cn.hutool.poi.excel.ExcelUtil.getWriter(excelPath + "/" + excelName);
        writer.addHeaderAlias("orderNo", "订单号");
        writer.addHeaderAlias("msg", "错误原因");
        writer.addHeaderAlias("batchNo", "批次号");
        // 一次性写出内容，使用默认样式，强制输出标题
        writer.write(dealResults, true);
        // 关闭writer，释放内存
        writer.close();
        return true;
    }

    /**
     * m40交易导入
     * @param sheet0 excel内容
     * @param batchExcelPath oss的excel文件地址
     * @param operator 操作人
     */
    private Result<JSONObject> doM40TransactionImport(List<M40ExcelTradeInfoDTO> sheet0, String batchExcelPath, String operator) {
        String batchNo = getBatchNo();

        ArrayList<JSONObject> failList = new ArrayList<>();
        int successNum = 0;

        HashMap<String, M40ExcelTradeInfoDTO> combineTrades= MapUtil.newHashMap(sheet0.size()/10);
        for (M40ExcelTradeInfoDTO row : sheet0) {
            //合伙人数据不导入
            if (StringUtil.isNotEmpty(row.getPartnerName()) && row.getPartnerName().length() > 1) {
                continue;
            }
            //空行
            if (StringUtil.isAllBlank(row.getOrderNo(), row.getMerchantNo(), row.getMerchantName(), row.getTerminalSn(), row.getCardType(), row.getTransactionTime())) {
                continue;
            }
            if (StringUtil.isAnyBlank(row.getOrderNo(), row.getMerchantNo(), row.getMerchantName(), row.getTerminalSn(), row.getCardType(), row.getTransactionTime())) {
                JSONObject errorItem = new JSONObject();
                errorItem.put("orderNo", row.getOrderNo());
                errorItem.put("msg", "数据不完整，导入失败");
                errorItem.put("batchNo", batchNo);
                failList.add(errorItem);
                continue;
            }

            boolean zeroFee = BigDecimal.ZERO.compareTo(row.getFee()) == 0;
            String key = row.getTerminalSn() + row.getMerchantNo() + row.getCardType() + (zeroFee ? "0" : "1");
            M40ExcelTradeInfoDTO existTrade = combineTrades.get(key);
            if (Objects.isNull(existTrade)) {
                existTrade = ObjectUtil.clone(row);
                existTrade.setTradeCount(1);
                existTrade.setOrderNos(row.getOrderNo());
                combineTrades.put(key, existTrade);
            } else {
                //报表中可能出现重复的数据做跳过
                if(StringUtil.contains(existTrade.getOrderNos(), row.getOrderNo())){
                    continue;
                }
                existTrade.setAmount(existTrade.getAmount().add(row.getAmount()));
                if (!zeroFee) {
                    existTrade.setFee(existTrade.getFee().add(row.getFee()));
                }
                existTrade.setTradeCount(existTrade.getTradeCount() + 1);
                existTrade.setOrderNos(existTrade.getOrderNos() + "," + row.getOrderNo());
                if (StringUtils.compare(existTrade.getTransactionTime(), row.getTransactionTime()) > 0) {
                    existTrade.setTransactionTime(row.getTransactionTime());
                }
            }
        }

        int index = -1;
        for (String key : combineTrades.keySet()) {
            if (!TransactionConstant.M40TradeImport.OPEN.getCode().equals(acmGlobalParams.getM40TradeImport())) {
                KLog.info("m40交易导入开关关闭，导入退出");
                break;
            }
            index++;
            M40ExcelTradeInfoDTO row = combineTrades.get(key);
            //用订单号排重
            PayTransactionIndex payTransactionRecord= transactionPayTransactionIndexMapper.getByOrderNo(row.getOrderNo());
            if(Objects.nonNull(payTransactionRecord)){
                KLog.info("m40交易导入排重跳过,{}", row.getOrderNo());
                continue;
            }
            TradeSupplementDTO tradeSupplementDTO = new TradeSupplementDTO();
            try {
                tradeSupplementDTO.setChannelId(ChannelIdEnums.yxy_jl_m40.getCode());
                LocalDateTime transactionTime = DateFormat.DATETIME_DEFAULT.parse(row.getTransactionTime());
                tradeSupplementDTO.setTransactionTime(transactionTime);
                tradeSupplementDTO.setOrderNo(row.getOrderNo());
                //没有流水号，订单号和流水号设置一样
                tradeSupplementDTO.setTransactionNo(row.getOrderNo());
                //系统参考号 待定
                tradeSupplementDTO.setSysRef("无");
                tradeSupplementDTO.setMerchantNo(row.getMerchantNo());
                tradeSupplementDTO.setMerchantName(row.getMerchantName());
                tradeSupplementDTO.setTerminalSn(row.getTerminalSn());
                tradeSupplementDTO.setTerminalId(row.getTerminalSn());
                if (Objects.nonNull(row.getAmount())) {
                    tradeSupplementDTO.setTransactionAmount(row.getAmount());
                }
                String cardFlag = getM40CardType(row.getCardType());
                tradeSupplementDTO.setCardFlag(cardFlag);
                if (Objects.nonNull(row.getFee())) {
                    tradeSupplementDTO.setTransactionFee(row.getFee());
                }
                tradeSupplementDTO.setTradeCount(row.getTradeCount());
                tradeSupplementDTO.setOrderNos(row.getOrderNos());

                tradeSupplementDTO.setOperator(operator);
                tradeSupplementDTO.setBatchExcelPath(batchExcelPath);
                tradeSupplementDTO.setBatchNo(batchNo);
                KLog.info("批量补录，处理第[{}]条数据,入参:{}", index, tradeSupplementDTO.toJSON());
                Result result = this.tradeSupplement(tradeSupplementDTO);
                if (!result.isSuccess()) {
                    JSONObject errorItem=new JSONObject();
                    errorItem.put("orderNo", row.getOrderNo());
                    errorItem.put("msg", result.getDesc());
                    errorItem.put("batchNo", batchNo);
                    failList.add(errorItem);
                    continue;
                }
                successNum++;
            } catch (Exception e) {
                KLog.warn("m40交易补录, 生成补录对象异常，{}", e);
                JSONObject errorItem=new JSONObject();
                errorItem.put("orderNo", row.getOrderNo());
                errorItem.put("msg", e.getMessage());
                errorItem.put("batchNo", batchNo);
                failList.add(errorItem);
            }
        }
        JSONObject jResult=new JSONObject();
        jResult.put("successNum", successNum);
        jResult.put("errorList", failList);
        return Result.builder().success(jResult);
    }

    /**
     * 处理补录交易
     * @param file 文件
     * @param operator 操作人
     * @param dealFunction Excel处理器
     * @param headerCheck 页头检查处理器
     * @return 处理结果
     */
    private Result dealExcelTransactionDate(MultipartFile file, String operator,
                                            Function<ExcelDataAndBatchDateDTO, TradeSupplementDTO> dealFunction,
                                            Function<ArrayList<String>, String> headerCheck){
        Result<String> uploadResult = upload(file,UploadTypeEnum.BATCH_TRADE_SUPPLEMENT_EXCEL);
        if (!uploadResult.isSuccess()){
            return uploadResult;
        }
        String batchExcelPath = uploadResult.getData();
        KLog.info("批量补录batchExcelPath:{}",batchExcelPath);
        LinkedHashMap<String, ArrayList<ArrayList<String>>> stringArrayListLinkedHashMap;
        try {
            if (file.getResource().getFilename().endsWith(".xlsx")) {
                stringArrayListLinkedHashMap = ExcelUtil.readExcelForXlsx(file.getInputStream(), 0,true);
            } else {
                return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR,"文件格式错误，请核对后再上传！");
            }
        } catch (Exception e) {
            return Result.builder().fail(BizCodeMessages.PARAMETER_ERR, "excel读取失败");
        }

        String batchNo = getBatchNo();
        ArrayList<ArrayList<String>> sheet0 = stringArrayListLinkedHashMap.get("Sheet0");
        if(Objects.isNull(sheet0)) {
            String sheet0Key = stringArrayListLinkedHashMap.keySet().iterator().next();
            sheet0 = stringArrayListLinkedHashMap.get(sheet0Key);
        }
        if (sheet0.size() > 1000){
            return Result.builder().fail(BizCodeMessages.BATCH_TRADE_SUPPLEMENT_TOO_MUCH, "单次补录交易数量不能超过1000");
        }
        ArrayList<TradeSupplementDTO> failList = new ArrayList<>();
        int index = -1;
        for (ArrayList<String> row : sheet0) {
            index++;
            if (index == 0){
                String headerCheckResult=headerCheck.apply(row);
                if (StringUtil.isNotBlank(headerCheckResult)){
                    return Result.builder().fail(BizCodeMessages.EXCEL_HEADER_FORMAT_ERROR, "表头格式错误,"+ headerCheckResult);
                }
                continue;
            }
            ExcelDataAndBatchDateDTO excelDataAndBatchDate=new ExcelDataAndBatchDateDTO();
            excelDataAndBatchDate.setRow(row);
            excelDataAndBatchDate.setBatchExcelPath(batchExcelPath);
            excelDataAndBatchDate.setBatchNo(batchNo);
            excelDataAndBatchDate.setOperator(operator);
            TradeSupplementDTO tradeSupplementDTO = dealFunction.apply(excelDataAndBatchDate);
            if(Objects.nonNull(tradeSupplementDTO)){
                try {
                    KLog.info("批量补录，处理第[{}]条数据,入参:{}",index,tradeSupplementDTO.toJSON());
                    Result result = this.tradeSupplement(tradeSupplementDTO);
                    if (!result.isSuccess()) {
                        failList.add(tradeSupplementDTO);
                    }
                } catch (Exception e) {
                    failList.add(tradeSupplementDTO);
                }
            }else{
                Result.builder().fail(BizCodeMessages.VALIDATOR_ERR,"数据格式错误，请核对后再上传！");
            }
        }
        HashMap<String, String> resultMap = new HashMap<>();
        resultMap.put("successAmt", String.valueOf(sheet0.size()  - 1 - failList.size()));
        resultMap.put("failAmt", String.valueOf(failList.size()));
        resultMap.put("batchNo", batchNo);
        return Result.builder().success(resultMap);
    }

    @RoutingDataSource(DataSources.SLAVE_DB)
    @Override
    public Result tradeSupplementResultList(TradeSupplementResultListDTO tradeSupplementResultListDTO) {

        KLog.info("查询补录结果入参:{}",tradeSupplementResultListDTO.toJSON());
        PageFrom.startPage(tradeSupplementResultListDTO.getPageNum(),tradeSupplementResultListDTO.getPageSize());
        List<TransactionPayTransactionSupplementDTO> supplementList = mePayTransactionSupplementMapper.findSupplementList(tradeSupplementResultListDTO);
        if (CollectionUtils.isEmpty(supplementList)){
            return Result.builder().successPage(supplementList);
        }
        supplementList.forEach(supplement -> {
            supplement.setChannelName(ChannelIdEnums.getChannelNameByCode(supplement.getChannelId()));
            if (StringUtils.isNotBlank(supplement.getRequestParamDesc())){
                TradeSupplementDTO tradeSupplementDTO =
                        JSONObject.parseObject(supplement.getRequestParamDesc(),TradeSupplementDTO.class);
                supplement.setTradeSupplementDTO(tradeSupplementDTO);
            }
        });
        return Result.builder().successPage(supplementList);
    }

    @Override
    public Result queryClientPledgeList(QueryClientPledgeListDTO dto) {
        KLog.info("[查询终端（基梯）押金对账列表]入参:{}", dto.toString());
        Result checkResult = queryClientPledgeCommonCheck(dto);
        if (checkResult != null) {
            return checkResult;
        }
        if (dto.getPageSize() > 0) {
            PageFrom.startPage(dto.getPageNum(), dto.getPageSize());
        }
        List<QueryClientPledgeListVO> queryClientPledgeList = transactionMerchantMapper.queryClientPledgeList(dto);
        return Result.builder().successPage(queryClientPledgeList);
    }

    @RoutingDataSource(DataSources.SLAVE_DB)
    @Override
    public Result queryClientPledgeStatistics(QueryClientPledgeListDTO dto) {
        KLog.info("[查询终端（基梯）押金对账 统计]入参:{}", dto.toString());
        Result checkResult = queryClientPledgeCommonCheck(dto);
        if (checkResult != null) {
            return checkResult;
        }
        //数量统计
        QueryClientPledgeStatisticsVO clientPledgeStatistics = transactionMerchantMapper.queryClientPledgeStatistics(dto);
        if (clientPledgeStatistics == null) {
            clientPledgeStatistics = new QueryClientPledgeStatisticsVO();
        }
        //channel统计
        List<QueryClientPledgeStatisticsChannelVO> pledgeStatisticsChannelList = transactionMerchantMapper.queryClientPledgeChannelStatistics(dto);
        if (pledgeStatisticsChannelList.size() > 0) {
            int ePosCount = 0, bigPosCount = 0;
            for (QueryClientPledgeStatisticsChannelVO queryClientPledgeStatisticsChannel : pledgeStatisticsChannelList) {
                if (ChannelIdEnums.yxy_lkl_p45.getCode().intValue() == queryClientPledgeStatisticsChannel.getChannelId()) {
                    bigPosCount += queryClientPledgeStatisticsChannel.getCount();
                } else {
                    ePosCount += queryClientPledgeStatisticsChannel.getCount();
                }
            }
            clientPledgeStatistics.setEposCount(ePosCount + "");
            clientPledgeStatistics.setBigPosCount(bigPosCount + "");
        }
        return Result.builder().success(clientPledgeStatistics);
    }

    @RoutingDataSource(DataSources.SLAVE_DB)
    @Override
    public Result<QueryClientHomePageStatisticsVO> queryClientFirstPageStatistics(QueryClientFirstPageStatisticsDTO dto) {
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = LocalDate.now().atStartOfDay();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DateUtil.parsePatterns[1]);
        //今天
        TransactionCountYxyDTO todayCount = transactionPayTransactionIndexMapper.getClientTransactionByTime(dto.getClientNo(),
                startTime.format(dateTimeFormatter), endTime.format(dateTimeFormatter));
        //当月
        LocalDateTime startMonthTime = LocalDate.now().atStartOfDay().minusDays(LocalDate.now().getDayOfMonth() - 1);
        TransactionCountYxyDTO currentMonthCount = transactionPayTransactionIndexMapper.getClientTransactionByTime(dto.getClientNo(),
                startMonthTime.format(dateTimeFormatter), endTime.format(dateTimeFormatter));
        QueryClientHomePageStatisticsVO homePageStatisticsVO = new QueryClientHomePageStatisticsVO();
        if (Objects.nonNull(todayCount)) {
            if (Objects.nonNull(todayCount.getAmount())) {
                homePageStatisticsVO.setTodayAmountSum(todayCount.getAmount().toString());
            }
            if (Objects.nonNull(todayCount.getCount())) {
                homePageStatisticsVO.setTodayAmountCount(todayCount.getCount().toString());
            }
        }
        if (Objects.nonNull(currentMonthCount)) {
            if (Objects.nonNull(currentMonthCount.getAmount())) {
                homePageStatisticsVO.setMonthAmountSum(currentMonthCount.getAmount().toString());
            }
            if (Objects.nonNull(currentMonthCount.getCount())) {
                homePageStatisticsVO.setMonthAmountCount(currentMonthCount.getCount().toString());
            }
        }
        //获取可提余额
        Result<ClientInfo> clientInfoResult = clientService.getClientInfoByClientNo(dto.getClientNo());
        if (Objects.nonNull(clientInfoResult) && clientInfoResult.isSuccess() && Objects.nonNull(clientInfoResult.getData())) {
            AccountQueryDTO accountQueryDTO = new AccountQueryDTO();
            accountQueryDTO.setPlatNo(PlatNoEnums.YXY.getCode());
            accountQueryDTO.setUserBaseId(clientInfoResult.getData().getUserBaseId());
            Result<AccountQueryGroupVO> accountQueryGroupResult = accountService.queryAccountGroup(accountQueryDTO);
            boolean queryAccountSuccess = Objects.nonNull(accountQueryGroupResult) && accountQueryGroupResult.isSuccess() && Objects.nonNull(accountQueryGroupResult.getData());
            if (queryAccountSuccess && Objects.nonNull(accountQueryGroupResult.getData().getFundAccount())) {
                BigDecimal withdrawAmount = BigDecimal.ZERO;
                Integer allyFullAccountCode = AccountType.getAccountTypeByFullCode(PlatNoEnums.YXY.getCode(), Integer.parseInt(AccountType.ALLY.getShortAccountNo()));
                Integer allyProfitFullAccountCode = AccountType.getAccountTypeByFullCode(PlatNoEnums.YXY.getCode(), Integer.parseInt(AccountType.ALLY_PROFIT.getShortAccountNo()));
                for (FundQueryGroupVO fundQueryGroupVO : accountQueryGroupResult.getData().getFundAccount()) {
                    //返现或分润账户
                    if (allyFullAccountCode.equals(fundQueryGroupVO.getAccountType()) || allyProfitFullAccountCode.equals(fundQueryGroupVO.getAccountType())) {
                        withdrawAmount = withdrawAmount.add(fundQueryGroupVO.getAccountWithdrawable());
                    }
                }
                //可提金额
                homePageStatisticsVO.setWithdrawAmount(NumberUtil.doBigDecimalDownFormat(withdrawAmount));
            }
        }
        return Result.builder().success(homePageStatisticsVO);
    }

    /**
     * 条件检查
     * @param dto 查询条件
     * @return 如果检查失败 返回信息
     */
    private Result queryClientPledgeCommonCheck(QueryClientPledgeListDTO dto){
        if(StringUtils.isEmpty(dto.getClientNo())){
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR, "机构参数异常");
        }
        if (isQueryClientPledgeConditionAllBlank(dto)
                && StringUtils.isAnyBlank(dto.getBeginCreateTime(), dto.getEndCreateTime())) {
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR, "请选择时间范围");
        }
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DateUtil.parsePatterns[0]);
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DateUtil.parsePatterns[1]);
        if (StringUtils.isNotEmpty(dto.getBeginCreateTime())) {
            dto.setBeginCreateTime(LocalDate.parse(dto.getBeginCreateTime(), dateFormatter).atStartOfDay().format(dateTimeFormatter));
        }
        if (StringUtils.isNotEmpty(dto.getEndCreateTime())) {
            dto.setEndCreateTime(LocalDate.parse(dto.getEndCreateTime(), dateFormatter).plusDays(1).atStartOfDay().format(dateTimeFormatter));
        }
        if (isQueryClientPledgeConditionAllBlank(dto) &&
                LocalDateUtils.betweenDays(dto.getBeginCreateTime(), dto.getEndCreateTime()) > 31) {
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR, "请时间范围不要大于31天");
        }
        return null;
    }

    private boolean isQueryClientPledgeConditionAllBlank(QueryClientPledgeListDTO dto) {
        return StringUtils.isAllBlank(dto.getMerchantName(), dto.getMerchantNo(), dto.getPhone(), dto.getTerminalSn());
    }

    @Override
    public Result getAllCardFlag() {
        PayTransactionEnum.CardFlagEnum[] allCardFlag = PayTransactionEnum.CardFlagEnum.values();
        List<CardFlagVO> cardFlagVOList = new ArrayList<>();
        for (PayTransactionEnum.CardFlagEnum cardFlag : allCardFlag) {
            CardFlagVO cardFlagVO = new CardFlagVO();
            cardFlagVO.setCode(cardFlag.getCode());
            cardFlagVO.setName(cardFlag.getMsg());
            cardFlagVOList.add(cardFlagVO);
        }
        return Result.builder().success(cardFlagVOList);
    }

    @NoRepeatSubmit(time = 3)
    @Transactional
    @Override
    public Result correctCardFlag(CorrectCardFlagDTO correctCardFlagDTO) {

        if(!ChannelIdEnums.isEqualsAny(correctCardFlagDTO.getChannelId(),ChannelIdEnums.mpos,
                ChannelIdEnums.mpos_qr,ChannelIdEnums.mpos_sdk)){
            KLog.info("[修复卡类型],非蓝牙交易，不支持修改,入参：{}",correctCardFlagDTO.toJSON());
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR,"非蓝牙交易，不支持修改");
        }

        if(!PayTransactionEnum.CardFlagEnum.BORROW_CARD.getCode().equals(correctCardFlagDTO.getCardFlag())
                && !PayTransactionEnum.CardFlagEnum.CREDIT_CARD.getCode().equals(correctCardFlagDTO.getCardFlag())){
            KLog.info("[修复卡类型],不支持修改为该卡类型,入参：{}",correctCardFlagDTO.toJSON());
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR,"不支持修改为该卡类型");
        }

        //更新子表
        int tableIndex = PayTransactionConfig.getTransTableIndex(correctCardFlagDTO.getUserBaseId());

        PayTransaction payTransaction = transactionPayTransactionMapper.getByOrderNo(tableIndex,
                correctCardFlagDTO.getOrderNo());
        if (Objects.isNull(payTransaction)){
            return Result.builder().fail(BizCodeMessages.DB_QUERY_ERR,"未找到该笔交易");
        }


        if (correctCardFlagDTO.getTransactionFee().compareTo(payTransaction.getTransactionAmount()) >= 0){
            KLog.info("[修复卡类型],手续费应低于交易额,入参：{}",correctCardFlagDTO.toJSON());
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR,"手续费应低于交易额");
        }

        //时间校验
        boolean updateFlag = checkTime(payTransaction.getTransactionTime().toLocalDate());

        if (!updateFlag){
            KLog.info("[修复卡类型],时间校验未通过，入参：{}",correctCardFlagDTO.toJSON());
            return Result.builder().fail(BizCodeMessages.VALIDATOR_ERR,"时间校验未通过,仅支持更改当月交易或5号前更改上月交易");
        }

        //记录更新信息，状态为：初始
        Integer mercahntUpdateId =
                saveUpdate(correctCardFlagDTO,payTransaction, MerchantUpdateEnum.Status.STATUS_INIT.getCode());

        try {

            updatePayTransaction(correctCardFlagDTO, tableIndex, payTransaction);
            //同步快收
            boolean sendSuccessFlag = true;
            if (ChannelIdEnums.isEqualsAny(correctCardFlagDTO.getChannelId(), ChannelIdEnums.mpos,
                    ChannelIdEnums.mpos_qr, ChannelIdEnums.epos, ChannelIdEnums.epos_p30, ChannelIdEnums.p35)) {
                PayTransaction dbPayTransaction =
                        transactionPayTransactionMapper.getByOrderNo(tableIndex, payTransaction.getOrderNo());
                sendSuccessFlag = sendMqPayTransaction(dbPayTransaction);
            }
            //记录状态为失败
            if (!sendSuccessFlag) {
                updateMerchantUpdateStatus(payTransaction.getPlatNo(), mercahntUpdateId,
                        MerchantUpdateEnum.Status.STATUS_FAIL.getCode());
                return Result.builder().fail(BizCodeMessages.TRANSACTION_UPDATE_FAIL);
            }
        } catch (Exception e) {
            //记录状态为失败
            updateMerchantUpdateStatus(payTransaction.getPlatNo(), mercahntUpdateId,
                    MerchantUpdateEnum.Status.STATUS_FAIL.getCode());
            throw e;
        }

        //更新主表
        PayTransactionIndexExample example = new PayTransactionIndexExample();
        example.createCriteria()
                .andOrderNoEqualTo(correctCardFlagDTO.getOrderNo())
                .andCardFlagNotEqualTo(correctCardFlagDTO.getCardFlag());
        List<PayTransactionIndex> payTransactionIndexList = payTransactionIndexMapper.selectByExample(example);

        if (CollectionUtils.isNotEmpty(payTransactionIndexList)){

            Integer payTransactionIndexId = payTransactionIndexList.get(0).getPayTransactionIndexId();
            try {

                updatePayTransactionIndex(payTransactionIndexId, correctCardFlagDTO.getCardFlag());
                //同步快收
                if (ChannelIdEnums.isEqualsAny(correctCardFlagDTO.getChannelId(), ChannelIdEnums.mpos,
                        ChannelIdEnums.mpos_qr, ChannelIdEnums.epos, ChannelIdEnums.epos_p30, ChannelIdEnums.p35)){

                    PayTransactionIndex payTransactionIndex =
                            payTransactionIndexMapper.selectByPrimaryKey(payTransactionIndexId);

                    boolean sendIndexSuccessFlag  = sendMqPayTransactionIndex(payTransactionIndex);
                    //记录状态为失败
                    if (!sendIndexSuccessFlag){
                        updateMerchantUpdateStatus(payTransaction.getPlatNo(), mercahntUpdateId,
                                MerchantUpdateEnum.Status.STATUS_FAIL.getCode());
                        return Result.builder().fail(BizCodeMessages.TRANSACTION_UPDATE_FAIL);
                    }
                }
            }catch (Exception e){
                //记录状态为失败
                updateMerchantUpdateStatus(payTransaction.getPlatNo(), mercahntUpdateId,
                        MerchantUpdateEnum.Status.STATUS_FAIL.getCode());
                throw e;
            }
        }

        //记录状态为成功
        updateMerchantUpdateStatus(payTransaction.getPlatNo(),mercahntUpdateId,
                MerchantUpdateEnum.Status.STATUS_SUCC.getCode());

        return Result.builder().success();

    }

    @Override
    public Result getAllRate() {
        PayRateEnum[] allRate = PayRateEnum.values();
        List<RateVO> payRateList = new ArrayList<>();
        for (PayRateEnum payRateEnum : allRate) {
            RateVO rateVO = new RateVO();
            rateVO.setShotCode(payRateEnum.getShotCode());
            rateVO.setFullCode(payRateEnum.getFullCode());
            rateVO.setRateStr(payRateEnum.getRateStr());
            payRateList.add(rateVO);
        }
        return Result.builder().success(payRateList);
    }

    @Override
    public Result getAllTransactionStatus() {
        PayTransactionEnum.TransactionStatus[] allStatus = PayTransactionEnum.TransactionStatus.values();
        List<TransactionStatusVO> transactionStatusVOList = new ArrayList<>();
        for (PayTransactionEnum.TransactionStatus payRateEnum : allStatus) {
            TransactionStatusVO transactionStatusVO = new TransactionStatusVO();
            transactionStatusVO.setCode(payRateEnum.getCode());
            transactionStatusVO.setMsg(payRateEnum.getMsg());
            transactionStatusVOList.add(transactionStatusVO);
        }
        return Result.builder().success(transactionStatusVOList);
    }

    /**
     * 用于执行分表查询的任务类
     */
    class DoQueryPayTransaction implements Callable<List<PayTransaction>>{
        /**
         * 表下标
         */
        int tableIndex;
        /**
         * 查询条件
         */
        TradeManageDTO dto;

        public DoQueryPayTransaction(int tableIndex, TradeManageDTO dto) {
            this.tableIndex = tableIndex;
            this.dto = dto;
        }

        @Override
        public List<PayTransaction> call() {
            return transactionPayTransactionMapper.queryPayTransactionList(dto, tableIndex);
        }
    }

    /**
     * 请求补录
     */
    private Result toSupplement(TradeSupplementDTO tradeSupplementDTO){

        Result result = Result.builder().fail();
        if (ChannelIdEnums.yxy_lkl_p42.isEquals(tradeSupplementDTO.getChannelId())) {
            String paramLakala = formatTradeLakala(tradeSupplementDTO);
            String urlLakala = acmTransactionParams.getKaolakuaishouHost() + DEAL_TRADE_LAKALA_P42_URL;
            result = initiateRequest(paramLakala, urlLakala, tradeSupplementDTO.getChannelId());
        } else if (ChannelIdEnums.yxy_lkl_p40.isEquals(tradeSupplementDTO.getChannelId())
                || ChannelIdEnums.yxy_lkl_p45.isEquals(tradeSupplementDTO.getChannelId())
                || ChannelIdEnums.k40.isEquals(tradeSupplementDTO.getChannelId())
                || ChannelIdEnums.k45.isEquals(tradeSupplementDTO.getChannelId())) {
            String paramLakala = formatTradeLakala(tradeSupplementDTO);
            String urlLakala = acmTransactionParams.getKaolakuaishouHost() + DEAL_TRADE_LAKALA_URL;
            result = initiateRequest(paramLakala, urlLakala, tradeSupplementDTO.getChannelId());
        } else if (ChannelIdEnums.yxy_sft_s40.isEquals(tradeSupplementDTO.getChannelId())) {
            List<String> paramListSft = formatTradeSft(tradeSupplementDTO);
            String urlSft = acmGlobalParams.getLingyunHost() + DEAL_TRADE_SFT_URL;
            for (String paramSft : paramListSft) {
                result = initiateRequest(paramSft, urlSft, tradeSupplementDTO.getChannelId());
                if (!result.isSuccess()) {
                    KLog.warn("处理盛付通推送失败，paramSft:{}",paramSft);
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        } else if (ChannelIdEnums.yxy_ls_l40.isEquals(tradeSupplementDTO.getChannelId())) {
            String paramJl = formatTradeJl(tradeSupplementDTO);
            String urlJl = acmGlobalParams.getLingyunHost() + DEAL_TRADE_JL_URL;
            result = initiateRequest(paramJl, urlJl, tradeSupplementDTO.getChannelId());
        } else if (ChannelIdEnums.yxy_dy_d40.isEquals(tradeSupplementDTO.getChannelId())) {
            String paramDy = formatTradeDy(tradeSupplementDTO);
            String urlDy = acmGlobalParams.getLingyunHost() + DEAL_TRADE_DY_URL;
            result = initiateRequest(paramDy, urlDy, tradeSupplementDTO.getChannelId());
        }  else if (ChannelIdEnums.yxy_ldys_y40.isEquals(tradeSupplementDTO.getChannelId())) {
            String paramDy = formatTradeY40(tradeSupplementDTO);
            String urlDy = acmGlobalParams.getLingyunHost() + DEAL_TRADE_Y40_URL;
            result = initiateRequest(paramDy, urlDy, tradeSupplementDTO.getChannelId());
        } else {
            result = Result.builder().fail(BizCodeMessages.TRANSACTION_UNKNOW_PRODUCT,"未知的产品类型");
        }
        return result;
    }

    /**
     * 格式化拉卡拉交易推送
     */
    private String formatTradeLakala(TradeSupplementDTO tradeSupplementDTO) {

        KLog.info("格式化拉卡拉交易推送入参:{}", tradeSupplementDTO.toJSON());
        LakalaEposPayDTO lakalaEposPayDTO = new LakalaEposPayDTO();
        lakalaEposPayDTO.setTransactionNo(tradeSupplementDTO.getTransactionNo());
        lakalaEposPayDTO.setMerchantNo(tradeSupplementDTO.getMerchantNo());
        lakalaEposPayDTO.setTermId(tradeSupplementDTO.getTerminalId());
        lakalaEposPayDTO.setSn(tradeSupplementDTO.getTerminalSn());
        lakalaEposPayDTO.setTransTime(DateFormat.DATETIME_COMPACT.format(tradeSupplementDTO.getTransactionTime()));
        lakalaEposPayDTO.setTransAmt(String.valueOf(tradeSupplementDTO.getTransactionAmount().multiply(new BigDecimal("100")).setScale(0)));
        String cardFlag = PayTransactionEnum.CardFlagEnum.getCodeLakalaByCode(tradeSupplementDTO.getCardFlag());
        lakalaEposPayDTO.setCrdflg(cardFlag);
        lakalaEposPayDTO.setProductType(LakalaEposPayDTO.PRODUCT_TYPE);
        String transType = tradeSupplementDTO.getTransType();
        if (!LklTransTypeEnum.TYPE_412001.getTransType().equals(transType)) {
            transType = LklTransTypeEnum.TYPE_012001.getTransType();
        }
        lakalaEposPayDTO.setTransType(transType);
        lakalaEposPayDTO.setTransStatus(LakalaEposPayDTO.TRANS_STATUS);
        lakalaEposPayDTO.setSrefNo(tradeSupplementDTO.getSysRef());
        KLog.info("格式化拉卡拉交易推送出参:{}", lakalaEposPayDTO.toJSON());
        return lakalaEposPayDTO.toJSON();
    }

    /**
     * 格式化盛付通交易推送
     */
    private List<String> formatTradeSft(TradeSupplementDTO tradeSupplementDTO) {

        KLog.info("格式化盛付通交易推送入参:{}", tradeSupplementDTO.toJSON());
        List<String> paramList = new ArrayList<>();
        int amt = tradeSupplementDTO.getTransactionAmount().multiply(new BigDecimal("100")).intValue();
        Map<String,String> codeSft = PayTransactionEnum.CardFlagEnum.getCodeSftByCode(tradeSupplementDTO.getCardFlag());
        String cardType = codeSft.get("cardType");
        String issuerCode = codeSft.get("issuerCode");
        String txnTime = DateFormat.DATETIME_COMPACT.format(tradeSupplementDTO.getTransactionTime());
        DecimalFormat format = new DecimalFormat("0.00");
        Double feeOlde = tradeSupplementDTO.getTransactionFee().doubleValue();
        String fee = format.format(feeOlde);
        Double stlAmtOlde = tradeSupplementDTO.getSettlementAmount().doubleValue();
        Double amtOlde = tradeSupplementDTO.getTransactionAmount().doubleValue();
        String stlAmt = format.format(stlAmtOlde);
        String amt1 = format.format(amtOlde);
        String param = "cur=CNY&batchNo=000001&orderId=" + tradeSupplementDTO.getOrderNo() +
                "&traceNo=000017&sign=lakala.2021&amt=" + amt +
                "&txnRef=1000521837&txnType=PUR&terminalId=13406598&shortPan=0310&merchantId=820134359990031&extData={\"ss\":\" 866737056833957\",\"orderType\":\"0\",\"de\":\"0\",\"cardType\":\"" + cardType +
                "\",\"discountAmount\":\"\",\"merchantName\":\"" + tradeSupplementDTO.getMerchantName() +
                "\",\"payAmount\":\"\",\"policyId\":\"101841\",\"payChannel\":\"57\",\"issuerCode\":\"" + issuerCode +
                "\",\"sn\":\"" + tradeSupplementDTO.getTerminalSn() +
                "\",\"hashPan\":\"abe911c9b1343e2c75a73ece619b4fb3a1ca7ab6\",\"authRef\":\"000858307727\",\"authOrderNo\":\"\",\"sdpMerchant\":\"" + tradeSupplementDTO.getMerchantNo() +
                "\"}&txnTime=" + txnTime + "&respCode=00";
        paramList.add(param);
        KLog.info("格式化盛付通交易推送出参:{}", param);
        if (LklTransTypeEnum.TYPE_412001.getTransType().equals(tradeSupplementDTO.getTransType())){
            String paramPle = "businessType=2&dataJson={\"cashPledgeAmt\":\"" + amt1 +
                    "\",\"merchantId\":\"" + tradeSupplementDTO.getMerchantNo() +
                    "\",\"tradeNo\":\"" + tradeSupplementDTO.getOrderNo() + "\"}&mac=lakala.2021";
            KLog.info("格式化盛付通押金结算推送出参:{}", paramPle);
            paramList.add(paramPle);
            return paramList;
        }
        String paramStl = "businessType=4&dataJson={\"amt\":\"" + amt1 +
                "\",\"fee\":\"" + fee + "\",\"merchantId\":\"" +
                tradeSupplementDTO.getMerchantNo() + "\",\"orderId\":\"" +
                tradeSupplementDTO.getOrderNo() + "\",\"stlAmt\":\"" + stlAmt +
                "\",\"stlBillNo\":\"32342437211106151240689633623905\",\"stlType\":\"T0\",\"txnTime\":\"" + txnTime +
                "\"}&mac=lakala.2021";
        KLog.info("格式化盛付通消费结算推送出参:{}", paramStl);
        paramList.add(paramStl);
        return paramList;
    }

    /**
     * 格式化嘉联交易推送
     */
    private String formatTradeJl(TradeSupplementDTO tradeSupplementDTO) {

        KLog.info("格式化嘉联交易推送入参:{}", tradeSupplementDTO.toJSON());
        TradePushDTO tradePushDTO = new TradePushDTO();
        String amt = tradeSupplementDTO.getTransactionAmount().multiply(new BigDecimal("100")).toString();
        tradePushDTO.setAmount(amt);
        String transType = tradeSupplementDTO.getTransType();
        if (LklTransTypeEnum.TYPE_412001.getTransType().equals(transType)) {
            transType = TradePushDTO.PLEDGE_CASH_TYPE_JL;
        }
        tradePushDTO.setBusi_sub_type(transType);
        tradePushDTO.setCard_no(tradeSupplementDTO.getTransactionCardNo());
        tradePushDTO.setMerch_no(tradeSupplementDTO.getMerchantNo());
        tradePushDTO.setOrder_id(tradeSupplementDTO.getOrderNo());
        String payType = PayTransactionEnum.CardFlagEnum.getCodeJlByCode(tradeSupplementDTO.getCardFlag());
        tradePushDTO.setPay_type(payType);
        tradePushDTO.setPos_sn(tradeSupplementDTO.getTerminalSn());
        tradePushDTO.setRefer_no(tradeSupplementDTO.getSysRef());
        tradePushDTO.setRet_code(JLResult.success().getRet_code());
        tradePushDTO.setTerm_no(tradeSupplementDTO.getTerminalId());
        tradePushDTO.setTrans_id(tradeSupplementDTO.getTransactionNo());
        tradePushDTO.setFee_amount(tradeSupplementDTO.getTransactionFee().toString());
        tradePushDTO.setTrans_time(DateFormat.DATETIME_COMPACT.format(tradeSupplementDTO.getTransactionTime()));
        tradePushDTO.setSign(TradePushDTO.SIGN);
        KLog.info("格式化嘉联交易推送出参:{}", tradePushDTO.toJSON());
        return tradePushDTO.toJSON();
    }

    /**
     * 格式化电银交易推送
     */
    private String formatTradeDy(TradeSupplementDTO tradeSupplementDTO) {

        KLog.info("格式化电银交易推送入参:{}", tradeSupplementDTO.toJSON());
        DyTransactionVO dyTransactionVO = new DyTransactionVO();
        dyTransactionVO.setMERC_NM(tradeSupplementDTO.getMerchantName());
        dyTransactionVO.setMERC_ID(tradeSupplementDTO.getMerchantNo());
        dyTransactionVO.setTRM_NO(tradeSupplementDTO.getTerminalId());
        dyTransactionVO.setTXN_TM(DateFormat.DATETIME_COMPACT.format(tradeSupplementDTO.getTransactionTime()));
        dyTransactionVO.setSN_NO(tradeSupplementDTO.getTerminalSn());
        Map<String,String> codeDy = PayTransactionEnum.CardFlagEnum.getCodeDyByCode(tradeSupplementDTO.getCardFlag());
        String payType = codeDy.get("payType");
        if(LklTransTypeEnum.TYPE_412001.getTransType().equals(tradeSupplementDTO.getTransType())){
            payType = DY_PLEDGE_CASH;
        }
        String cardFlag = codeDy.get("cardFlag");
        dyTransactionVO.setTXN_CD(payType);
        dyTransactionVO.setCRD_FLG(cardFlag);
        dyTransactionVO.setLOG_NO(tradeSupplementDTO.getOrderNo());
        dyTransactionVO.setSREF_NO(tradeSupplementDTO.getSysRef());
        dyTransactionVO.setFEE_ORG_NO(tradeSupplementDTO.getTransactionFee().toString());
        dyTransactionVO.setTXN_STS(DyTransactionVO.TXN_STS_SUCCESS);
        dyTransactionVO.setTXN_AMT(tradeSupplementDTO.getTransactionAmount().doubleValue());
        KLog.info("格式化电银交易推送出参:{}", dyTransactionVO.toJSON());
        return dyTransactionVO.toJSON();
    }

    /**
     * 格式化联动优势交易推送数据
     */
    private String formatTradeY40(TradeSupplementDTO tradeSupplementDTO) {
        KLog.info("格式化联动优势交易推送入参:{}", tradeSupplementDTO.toJSON());
        JSONObject y40Trade=new JSONObject();
        y40Trade.put("source", "tradeSupplement");
        y40Trade.put("rpid", "TZ010"+System.currentTimeMillis());
        y40Trade.put("reqPath","/trans/notify");
        y40Trade.put("userNo","8600202");
        LdysTradeDTO ldysTradeDTO=new LdysTradeDTO();
        ldysTradeDTO.setMerNo(tradeSupplementDTO.getMerchantNo());
        ldysTradeDTO.setMerName(tradeSupplementDTO.getMerchantName());
        ldysTradeDTO.setSerial(tradeSupplementDTO.getTransactionNo());
        ldysTradeDTO.setAmount(tradeSupplementDTO.getTransactionAmount().multiply(new BigDecimal("100")).toBigInteger().toString());

        if(Objects.nonNull(tradeSupplementDTO.getTransactionFee())){
            ldysTradeDTO.setTradeCharges(tradeSupplementDTO.getTransactionFee().multiply(new BigDecimal("100")).toBigInteger().toString());
        }

        if(StringUtils.equals(tradeSupplementDTO.getCardFlag(), PayTransactionEnum.CardFlagEnum.WECHAT.getCode())){
            ldysTradeDTO.setPayType("2");
        }else if(StringUtils.equals(tradeSupplementDTO.getCardFlag(), PayTransactionEnum.CardFlagEnum.ALIPAY.getCode())){
            ldysTradeDTO.setPayType("3");
        }else if(StringUtils.equals(tradeSupplementDTO.getCardFlag(), PayTransactionEnum.CardFlagEnum.UNIONCODE.getCode())){
            ldysTradeDTO.setPayType("1");
        }else if(StringUtils.equals(tradeSupplementDTO.getCardFlag(), PayTransactionEnum.CardFlagEnum.BORROW_CARD.getCode())){
            ldysTradeDTO.setCardType("1");
        }else if(StringUtils.equals(tradeSupplementDTO.getCardFlag(), PayTransactionEnum.CardFlagEnum.CREDIT_CARD.getCode())){
            ldysTradeDTO.setCardType("2");
        }
        ldysTradeDTO.setTerNo(tradeSupplementDTO.getTerminalId());
        ldysTradeDTO.setSn(tradeSupplementDTO.getTerminalSn());
        ldysTradeDTO.setPan(tradeSupplementDTO.getTransactionCardNo());
        ldysTradeDTO.setStatus("1");
        if (Objects.nonNull(tradeSupplementDTO.getTransactionTime())) {
            String transactionTime = LocalDateUtils.formatByDefaultFormat(tradeSupplementDTO.getTransactionTime());
            ldysTradeDTO.setLocalDate(transactionTime.split(" ")[0]);
            ldysTradeDTO.setLocalTime(transactionTime.split(" ")[1]);
        }
        if (Objects.nonNull(tradeSupplementDTO.getD0Fee())) {
            ldysTradeDTO.setTradeChargesFj(tradeSupplementDTO.getD0Fee().multiply(new BigDecimal("100")).toBigInteger().toString());
        }

        if(StringUtils.equals(tradeSupplementDTO.getTransType(), LklTransTypeEnum.TYPE_412001.getTransType())){
            ldysTradeDTO.setMsgType("Y007");
        }

//        Map<String, String> payParams = payTypeApiService.getPayParams(PayTypeEnum.LIANDONG_Y40, PayFromEnum.merchant);
//        try {
//            y40Trade.put("data", LdysSignUtil.encrypt(ldysTradeDTO.toJSON(), LdysSignUtil.getPublicKey(payParams.get("pubKey"))));
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        KLog.info("格式化联动优势交易推送出参:{}", y40Trade.toJSONString());
        return y40Trade.toJSONString();
    }

    /**
     * 发起请求
     */
    private Result initiateRequest(String param, String url, Short channelId) {

        String channelName = ChannelIdEnums.getChannelNameByCode(channelId);
        String response = "";
        JSONObject jsonObject = null;
        int i = 1;
        while (i < 4) {

            try {
                i++;
                KLog.info("【补录交易:{}】准备请求接口,url:{}, 请求参数:{}",channelName, url, param);
                //请求接口
                if (ChannelIdEnums.yxy_sft_s40.isEquals(channelId)) {
                    response = HttpClientUtil.httpPostRequestText(url, param);
                } else {
                    response = HttpClientUtil.httpPostRequest(url, param);
                }
                KLog.info("【补录交易:{}】接口响应:{},入参:{}",channelName, response, param);
                if (StringUtils.isNotBlank(response)) {
                    if (ChannelIdEnums.yxy_sft_s40.isEquals(channelId)
                            && response.equals("\"SUCCESS\"")) {
                        return Result.builder().success();
                    } else {
                        jsonObject = JSON.parseObject(response);
                        if ((ChannelIdEnums.yxy_lkl_p40.isEquals(channelId)
                                || ChannelIdEnums.yxy_lkl_p42.isEquals(channelId)
                                || ChannelIdEnums.k40.isEquals(channelId)
                                || ChannelIdEnums.yxy_lkl_p45.isEquals(channelId))
                                && jsonObject.getInteger("code") == 0) {
                            return Result.builder().success();
                        }
                        if (ChannelIdEnums.yxy_ls_l40.isEquals(channelId)
                                && jsonObject.getString("ret_code").equals("00")) {
                            return Result.builder().success();
                        }
                        if (ChannelIdEnums.yxy_dy_d40.isEquals(channelId)
                                && jsonObject.getString("code").equals("0000")) {
                            return Result.builder().success();
                        }
                        if (ChannelIdEnums.yxy_ldys_y40.isEquals(channelId)
                                && jsonObject.getString("ret_code").equals("0000")) {
                            return Result.builder().success();
                        }
                    }
                }
            } catch (Exception e) {
                if (i == 4) {
                    KLog.info("【补录交易:{}】请求异常:{},入参:{}",channelName, e.getMessage(), param);
                    return Result.builder().fail(BizCodeMessages.TRANSACTION_SAVE_TRADE_REQUEST_ERROR, "保存交易接口请求异常,请检查参数是否正确");
                }
            }

            if (i == 4) {
                if (StringUtils.isBlank(response)) {
                    KLog.info("【补录交易:{}】接口返回空,入参:{}",channelName, param);
                    return Result.builder().fail(BizCodeMessages.TRANSACTION_SAVE_TRADE_RESPONSE_NULL,"保存交易接口返回空,请检查参数是否正确");
                } else {
                    KLog.info("【补录交易:{}】接口响应未成功,入参:{}",channelName, param);
                    return Result.builder().fail(BizCodeMessages.TRANSACTION_SAVE_TRADE_RESPONSE_FAIL,"保存交易接口请求失败,请检查参数是否正确");
                }
            }
        }
        return Result.builder().success();
    }

    private String getBatchNo() {
        return DateFormat.DATETIME_COMPACT.format(LocalDateTime.now()) + RandomStringUtils.randomNumeric(5);
    }

    private void shareBatchTradeSupplementTemplateExport(List<Map<String, Object>> list) {
        Map<String, String> header = new LinkedHashMap<>();
        header.put("channelName", "产品类型");
        header.put("transactionTime", "交易时间");
        header.put("orderNo", "订单号");
        header.put("transactionNo", "流水号");
        header.put("sysRef", "系统参考号");
        header.put("merchantNo", "商户号");
        header.put("terminalSn", "机具号");
        header.put("terminalId", "终端号");
        header.put("transactionAmount", "金额（元）");
        header.put("cardFlag", "卡类型");
        header.put("transType", "交易类型");
        header.put("transactionFee", "手续费（元）");
        header.put("settlementAmount", "结算金额");
        header.put("merchantName", "商户名称");
        header.put("transactionCardNo", "交易卡号");
        header.put("d0Fee", "d0手续费");
        String[] headers = new String[header.size()];
        String[] fields = new String[header.size()];
        AtomicInteger index = new AtomicInteger(0);
        header.forEach((k, v) -> {
            headers[index.get()] = v;
            fields[index.getAndIncrement()] = k;
        });
        String fileName = org.apache.commons.lang3.StringUtils.join("补录交易模板");
        ExportExcelUtils.exportExcel(fileName, headers, fields, list, HttpHolderUtil.getRequest(), HttpHolderUtil.getResponse());
    }

    /**
     * 文件上传
     */
    private Result<String> upload(MultipartFile multipart, UploadTypeEnum fileType) {
        if (Objects.isNull(fileType)) {

            return Result.builder().fail(BizCodeMessages.FILEUPLOAD_OSS_FAIL, "文件类型不能为空");
        }
        String fileDir = fileType.getType();
        Result<String> result = Result.builder().success();
        try {
            OSSObject formOSS = ossClientUtil.steamUpload(multipart, fileDir + Generator.nextUUID());
            if (Objects.isNull(formOSS) || !formOSS.getResponse().isSuccessful()) {

                KLog.error("文件上传失败：{}", Objects.isNull(formOSS) ? "" : formOSS.toString());
                return Result.builder().fail(BizCodeMessages.FILEUPLOAD_OSS_FAIL,"文件上传失败");
            }
            result.setData(formOSS.getKey());
        } catch (Exception e) {

            KLog.error("文件上传失败", e);
            return Result.builder().fail(BizCodeMessages.FILEUPLOAD_OSS_FAIL,"文件上传失败");
        }

        return result;
    }

    /**
     * 更新index表卡类型
     * @param payTransactionIndexId
     * @param cardFlag
     */
    private void updatePayTransactionIndex(Integer payTransactionIndexId, String cardFlag){

        PayTransactionIndex upPayTransactionIndex = new PayTransactionIndex();
        upPayTransactionIndex.setPayTransactionIndexId(payTransactionIndexId);
        upPayTransactionIndex.setCardFlag(cardFlag);
        int upCount = payTransactionIndexMapper.updateByPrimaryKeySelective(upPayTransactionIndex);
        BizAssertUtils.assertOne(upCount, "更新交易主表失败");

    }

    /**
     * 记录更新信息
     * @param correctCardFlagDTO
     * @param payTransaction
     * @param status
     * @return
     */
    private Integer saveUpdate(CorrectCardFlagDTO correctCardFlagDTO,PayTransaction payTransaction,Short status){

        CorrectCardFlagDTO updateValue = new CorrectCardFlagDTO();
        updateValue.setCardFlag(correctCardFlagDTO.getCardFlag());
        updateValue.setTransactionFee(correctCardFlagDTO.getTransactionFee());
        updateValue.setRate(correctCardFlagDTO.getRate());
        String updateStrValue = updateValue.toJSON();

        CorrectCardFlagDTO oldValue = new CorrectCardFlagDTO();
        oldValue.setCardFlag(payTransaction.getCardFlag());
        oldValue.setTransactionFee(payTransaction.getTransactionFee());
        oldValue.setRate(payTransaction.getRate());
        String oldStrValue = oldValue.toJSON();

        if (PlatNoEnums.KLKS.getCode().equals(payTransaction.getPlatNo())){
            MerchantUpdate20 merchantUpdate20 = new MerchantUpdate20();
            merchantUpdate20.setUserBaseId(payTransaction.getUserBaseId());
            merchantUpdate20.setMerchantNo(payTransaction.getMerchantNo());
            merchantUpdate20.setContractId(payTransaction.getOrderNo());
            merchantUpdate20.setPlatNo(payTransaction.getPlatNo());
            merchantUpdate20.setChannelId(payTransaction.getChannelId());
            merchantUpdate20.setType(MerchantUpdateEnum.TypeEnum.CARD_FLAG.getCode());
            merchantUpdate20.setOldStrValue(oldStrValue);
            merchantUpdate20.setUpdateStrValue(updateStrValue);
            merchantUpdate20.setStatus(status);
            merchantUpdate20.setRemarks(REMARKS);
            int insertCount = merchantUpdate20Mapper.insertSelective(merchantUpdate20);
            BizAssertUtils.assertOne(insertCount, "插入更新记录失败");

            MerchantUpdate20Example update20Example = new MerchantUpdate20Example();
            update20Example.createCriteria()
                    .andChannelIdEqualTo(payTransaction.getChannelId())
                    .andPlatNoEqualTo(payTransaction.getPlatNo())
                    .andContractIdEqualTo(payTransaction.getOrderNo())
                    .andOldStrValueEqualTo(oldStrValue)
                    .andUpdateStrValueEqualTo(updateStrValue)
                    .andStatusEqualTo(status);
            List<MerchantUpdate20> merchantUpdate20List = merchantUpdate20Mapper.selectByExample(update20Example);
            return merchantUpdate20List.get(0).getMerchantUpdateId();
        }

        MerchantUpdate21 merchantUpdate21 = new MerchantUpdate21();
        merchantUpdate21.setUserBaseId(payTransaction.getUserBaseId());
        merchantUpdate21.setMerchantNo(payTransaction.getMerchantNo());
        merchantUpdate21.setContractId(payTransaction.getOrderNo());
        merchantUpdate21.setPlatNo(payTransaction.getPlatNo());
        merchantUpdate21.setChannelId(payTransaction.getChannelId());
        merchantUpdate21.setType(MerchantUpdateEnum.TypeEnum.CARD_FLAG.getCode());
        merchantUpdate21.setOldStrValue(oldStrValue);
        merchantUpdate21.setUpdateStrValue(updateStrValue);
        merchantUpdate21.setStatus(status);
        merchantUpdate21.setRemarks(REMARKS);
        int insertCount = merchantUpdate21Mapper.insertSelective(merchantUpdate21);
        BizAssertUtils.assertOne(insertCount, "插入更新记录失败");

        MerchantUpdate21Example update21Example = new MerchantUpdate21Example();
        update21Example.createCriteria()
                .andChannelIdEqualTo(payTransaction.getChannelId())
                .andPlatNoEqualTo(payTransaction.getPlatNo())
                .andContractIdEqualTo(payTransaction.getOrderNo())
                .andOldStrValueEqualTo(oldStrValue)
                .andUpdateStrValueEqualTo(updateStrValue)
                .andStatusEqualTo(status);
        List<MerchantUpdate21> merchantUpdate21List = merchantUpdate21Mapper.selectByExample(update21Example);
        return merchantUpdate21List.get(0).getMerchantUpdateId();
    }

    /**
     * 更新记录状态
     * @param platNo
     * @param mercahntUpdateId
     * @param status
     */
    private void updateMerchantUpdateStatus(Short platNo,Integer mercahntUpdateId,Short status){

        if (PlatNoEnums.KLKS.getCode().equals(platNo)){
            MerchantUpdate20 upMerchantUpdate20 = new MerchantUpdate20();
            upMerchantUpdate20.setMerchantUpdateId(mercahntUpdateId);
            upMerchantUpdate20.setStatus(status);
            int upCount20 = merchantUpdate20Mapper.updateByPrimaryKeySelective(upMerchantUpdate20);
            BizAssertUtils.assertOne(upCount20, "更新更新记录状态失败");
            return;
        }

        MerchantUpdate21 upMerchantUpdate21 = new MerchantUpdate21();
        upMerchantUpdate21.setMerchantUpdateId(mercahntUpdateId);
        upMerchantUpdate21.setStatus(status);
        int upCount21 = merchantUpdate21Mapper.updateByPrimaryKeySelective(upMerchantUpdate21);
        BizAssertUtils.assertOne(upCount21, "更新更新记录状态失败");
    }


    /**
     * 更新子表卡类型
     * @param correctCardFlagDTO
     * @param tableIndex
     * @param payTransaction
     */
    private void updatePayTransaction(
            CorrectCardFlagDTO correctCardFlagDTO,
            int tableIndex,
            PayTransaction payTransaction){

        PayTransaction upPayTransaction = new PayTransaction();
        upPayTransaction.setPayTransactionId(payTransaction.getPayTransactionId());
        upPayTransaction.setTransType(payTransaction.getTransType());
        upPayTransaction.setChannelId(payTransaction.getChannelId());
        upPayTransaction.setCardFlag(correctCardFlagDTO.getCardFlag());
        upPayTransaction.setTransactionCardType(correctCardFlagDTO.getCardFlag());
        upPayTransaction.setTransactionFee(correctCardFlagDTO.getTransactionFee());
        upPayTransaction.setRate(correctCardFlagDTO.getRate());
        upPayTransaction.setProfitFlag(KlksPayConfig.getProfitFlag(upPayTransaction));
        int upCount = transactionPayTransactionMapper.updateByIdNoVersion(tableIndex,upPayTransaction);
        BizAssertUtils.assertOne(upCount, "更新交易字表失败");

    }

    /**
     * 交易同步快收Mq
     * @param dbPayTransaction
     * @return
     */
    private boolean sendMqPayTransaction(PayTransaction dbPayTransaction){

        UserBaseExtExample extExample = new UserBaseExtExample();
        extExample.createCriteria().andIsDelNotEqualTo(DeleteFlag.TRUE)
                .andUserBaseIdEqualTo(dbPayTransaction.getUserBaseId())
                .andPlatNoEqualTo(dbPayTransaction.getPlatNo());
        List<UserBaseExt> userBaseExtList = userBaseExtMapper.selectByExample(extExample);

        if (CollectionUtils.isEmpty(userBaseExtList)){
            KLog.error(BizCodeMessages.DB_QUERY_ERR,"userBaseExt表未查询到数据，userBaseId={}",dbPayTransaction.getUserBaseId());
            return false;
        }

        PayTransactionPO klksPayTransactionPO = new PayTransactionPO();

        klksPayTransactionPO.setOrderNo(dbPayTransaction.getOrderNo());
        klksPayTransactionPO.setTransactionNo(dbPayTransaction.getTransactionNo());
        klksPayTransactionPO.setProductType(ChannelIdEnums.convertChannelIdToKuaiShouProductType(dbPayTransaction.getChannelId()));
        klksPayTransactionPO.setUserId(userBaseExtList.get(0).getOtherUserId());
        klksPayTransactionPO.setMerNo(dbPayTransaction.getMerchantNo());
        klksPayTransactionPO.setMerName(dbPayTransaction.getMerchantName());
        klksPayTransactionPO.setRate(dbPayTransaction.getRate());
        klksPayTransactionPO.setTransAmt(dbPayTransaction.getTransactionAmount());
        klksPayTransactionPO.setTransFee(dbPayTransaction.getTransactionFee());
        klksPayTransactionPO.setCardFlag(dbPayTransaction.getCardFlag());
        klksPayTransactionPO.setTermId(dbPayTransaction.getTerminalId());
        klksPayTransactionPO.setSid(dbPayTransaction.getSid());
        klksPayTransactionPO.setRevokeSid(dbPayTransaction.getRevokeSid());
        klksPayTransactionPO.setTransCardNo(dbPayTransaction.getTransactionCardNo());
        klksPayTransactionPO.setTransBank(dbPayTransaction.getTransactionBank());
        klksPayTransactionPO.setTransCardType(dbPayTransaction.getTransactionCardType());
        klksPayTransactionPO.setSettlementCardNo(dbPayTransaction.getSettlementCardNo());
        klksPayTransactionPO.setSettlementBank(dbPayTransaction.getSettlementBank());
        klksPayTransactionPO.setSettlementCardType(dbPayTransaction.getSettlementCardType());
        String status = PayTransactionEnum.TransactionStatus
                .convertLingyunPayTransactionStatusToKuaishou(dbPayTransaction.getStatus());
        klksPayTransactionPO.setStatus(status);
        klksPayTransactionPO.setTransTime(dbPayTransaction.getTransactionTime());
        klksPayTransactionPO.setTransMonth(dbPayTransaction.getTransactionMonth());
        klksPayTransactionPO.setTransDay(dbPayTransaction.getTransactionDay());
        klksPayTransactionPO.setCreateTime(dbPayTransaction.getCreateTime());
        klksPayTransactionPO.setUpdateTime(dbPayTransaction.getUpdateTime());
        klksPayTransactionPO.setVersion(dbPayTransaction.getVersion());
        klksPayTransactionPO.setProvinceCode(dbPayTransaction.getProvinceCode());
        klksPayTransactionPO.setCityCode(dbPayTransaction.getCityCode());
        klksPayTransactionPO.setDistrictCode(dbPayTransaction.getDistrictCode());
        klksPayTransactionPO.setAreaCode(dbPayTransaction.getAreaCode());
        klksPayTransactionPO.setSysRef(dbPayTransaction.getSysRef());
        klksPayTransactionPO.setAppSuccTime(dbPayTransaction.getFrontSuccessTime());
        klksPayTransactionPO.setRemarks(dbPayTransaction.getRemarks());
        klksPayTransactionPO.setTransType(dbPayTransaction.getTransType());

        UserAllResponse userAllResponse = new UserAllResponse();
        userAllResponse.setType(SendMqTypeEnums.updatePayTransaction.getType());
        userAllResponse.setDataString(JSONObject.toJSONString(klksPayTransactionPO));

        MqSendMessage sendMessage = new MqSendMessage();
        sendMessage.setMessageKey(SendMqTypeEnums.updatePayTransaction.getType() + "-" +
                dbPayTransaction.getOrderNo() + "-" + LocalDateTime.now());
        sendMessage.setTopic(MqEnum.MqTopicEnum.topic_ly_user_info);
        sendMessage.setTag(MqEnum.MqTagEnum.tag_ly_user_info);
        sendMessage.setSendMsg(JSONObject.toJSONString(userAllResponse));
        KLog.info("凌云更新交易同步到快收,发送MQ:{}", JSONObject.toJSONString(sendMessage));
        mcenterMqService.send(sendMessage);
        return true;
    }

    /**
     * 主表交易同步快收Mq
     * @param dbPayTransactionIndex
     * @return
     */
    private boolean sendMqPayTransactionIndex(PayTransactionIndex  dbPayTransactionIndex){

        UserBaseExtExample extExample = new UserBaseExtExample();
        extExample.createCriteria().andIsDelNotEqualTo(DeleteFlag.TRUE)
                .andUserBaseIdEqualTo(dbPayTransactionIndex.getUserBaseId())
                .andPlatNoEqualTo(dbPayTransactionIndex.getPlatNo());
        List<UserBaseExt> userBaseExtList = userBaseExtMapper.selectByExample(extExample);

        if (CollectionUtils.isEmpty(userBaseExtList)){
            KLog.error(BizCodeMessages.DB_QUERY_ERR,"userBaseExt表未查询到数据，userBaseId={}",dbPayTransactionIndex.getUserBaseId());
            return false;
        }

        com.yourenbang.lingyun.transaction.web.domain.dto.PayTransactionIndex klksPayTransactionIndex
                = new com.yourenbang.lingyun.transaction.web.domain.dto.PayTransactionIndex();

        klksPayTransactionIndex.setOrderNo(dbPayTransactionIndex.getOrderNo());
        klksPayTransactionIndex.setTransactionNo(dbPayTransactionIndex.getTransactionNo());
        klksPayTransactionIndex.setProductType(ChannelIdEnums.convertChannelIdToKuaiShouProductType(dbPayTransactionIndex.getChannelId()));
        klksPayTransactionIndex.setUserId(userBaseExtList.get(0).getOtherUserId());
        klksPayTransactionIndex.setMerNo(dbPayTransactionIndex.getMerchantNo());
        klksPayTransactionIndex.setTransAmt(dbPayTransactionIndex.getTransactionAmount());
        String status = PayTransactionEnum.TransactionStatus
                .convertLingyunPayTransactionStatusToKuaishou(dbPayTransactionIndex.getStatus());
        klksPayTransactionIndex.setStatus(status);
        klksPayTransactionIndex.setTransTime(dbPayTransactionIndex.getTransactionTime());
        klksPayTransactionIndex.setTransMonth(dbPayTransactionIndex.getTransactionMonth());
        //凌云：20220101   快收：01
        klksPayTransactionIndex.setTransDay(dbPayTransactionIndex.getTransactionDay().substring(6));
        klksPayTransactionIndex.setCreateTime(dbPayTransactionIndex.getCreateTime());
        klksPayTransactionIndex.setUpdateTime(dbPayTransactionIndex.getUpdateTime());

        UserAllResponse userAllResponse = new UserAllResponse();
        userAllResponse.setType(SendMqTypeEnums.updatePayTransactionIndex.getType());
        userAllResponse.setDataString(JSONObject.toJSONString(klksPayTransactionIndex));

        MqSendMessage sendMessage = new MqSendMessage();
        sendMessage.setMessageKey(SendMqTypeEnums.updatePayTransactionIndex.getType() + "-" +
                dbPayTransactionIndex.getOrderNo() + "-" + LocalDateTime.now());
        sendMessage.setTopic(MqEnum.MqTopicEnum.topic_ly_user_info);
        sendMessage.setTag(MqEnum.MqTagEnum.tag_ly_user_info);
        sendMessage.setSendMsg(JSONObject.toJSONString(userAllResponse));
        KLog.info("凌云更新index交易同步到快收,发送MQ:{}", JSONObject.toJSONString(sendMessage));
        mcenterMqService.send(sendMessage);
        return true;
    }

    /**
     * 时间校验
     * @param transactionTime
     * @return
     */
    private boolean checkTime(LocalDate transactionTime){

        //是否为本月
        boolean thisMonthFlag =
                transactionTime.isAfter(LocalDate.now().minusMonths(1).with(TemporalAdjusters.lastDayOfMonth())) &&
                transactionTime.isBefore(LocalDate.now().plusMonths(1).with(TemporalAdjusters.firstDayOfMonth()));

        if (thisMonthFlag){
            return thisMonthFlag;
        }

        //当前时间是否为5号前
        boolean fifthFlag =
                LocalDate.now().isBefore(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusDays(6));

        //是否为上月
        boolean lastMonthFlag =
                transactionTime.isAfter(LocalDate.now().minusMonths(2).with(TemporalAdjusters.lastDayOfMonth())) &&
                transactionTime.isBefore(LocalDate.now().minusMonths(1).with(TemporalAdjusters.firstDayOfMonth()));

        return fifthFlag && lastMonthFlag;
    }


    /**
     * 获取M40卡类型
     * @param cardType 从Excel中读取的卡类型字段值
     * @return 对应银小云卡类型
     */
    private String getM40CardType(String cardType) {
        String cardFlag = PayTransactionEnum.CardFlagEnum.getCodeByMsg(cardType);
        if (StringUtils.isBlank(cardFlag)) {
            if (M40_NEI_KA_CREDIT_NAME.equals(cardType)) {
                cardFlag = PayTransactionEnum.CardFlagEnum.CREDIT_CARD.getCode();
            } else if (M40_WEIXIN_DEBIT_NAME.equals(cardType)) {
                cardFlag = PayTransactionEnum.CardFlagEnum.WECHAT.getCode();
            }
        }
        return cardFlag;
    }

}
